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 a0862aacfc9..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -sudo: required - -language: java - -services: - - docker - -script: - - mvn clean install -Ddocker.showLogs -Pdocker -Pjacoco 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 2185fb23db7..b2fbc167625 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,116 @@ -# Java Chassis [![Build Status](https://travis-ci.org/ServiceComb/java-chassis.svg?branch=master)](https://travis-ci.org/ServiceComb/java-chassis?branch=master)[![Coverage Status](https://coveralls.io/repos/github/ServiceComb/java-chassis/badge.svg?branch=master)](https://coveralls.io/github/ServiceComb/java-chassis?branch=master) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.servicecomb/java-chassis-distribution/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); + } +} +``` -Project documentation is available on the [ServiceComb website][servicecomb-website]. +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) . + +# 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 + + mvn clean install -Pdocker -Pit - To build the docker image and run the integration tests with docker, you can use maven docker profile - - mvn clean install -Pdocker - If you are using docker machine, please use the following command - - mvn clean install -Pdocker -Pdocker-machine - -## Contact -Bugs: [issues](https://github.com/ServiceComb/java-chassis/issues) + mvn clean install -Pdocker -Pit -Pdocker-machine + +# 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 ebfea7c4c46..00000000000 --- a/common/common-javassist/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - common - 0.1.1-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 06c7849e433..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 479b91d9eb5..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 f9f0ffc03ee..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/JavassistUtils.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 e05c8e856ad..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 cb7fd506355..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 76aa28a1ba4..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 4cc5bc8b998..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 e176ba15663..00000000000 --- a/common/common-javassist/src/test/java/io/servicecomb/common/javassist/TestJavassistUtils.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 eb4d91cc382..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.1.1-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 33195031553..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 60fa8989f77..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 257aca43811..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 fcdd8fe14f5..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 f9709f1ed02..00000000000 --- a/common/common-protobuf/src/main/java/io/protostuff/runtime/RuntimeMapFieldProtobuf.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR 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 d1c5c075fd7..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 e688b3139ac..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 6d99fe39e41..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ParamFieldCodec.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.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 769a9b9b9d9..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ResultFieldCodec.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.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 82319a7e135..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardParamCodec.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.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 18faf1efe5e..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardResultCodec.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.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 c620ffbf92c..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 f3f8f56170d..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/definition/ProtobufManager.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.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 0ec52e0ecb6..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/AbstractDeserializer.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.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 548b6d5ede2..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 f56ca47fb39..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 dd787b66b75..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 4bb61fdb929..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ParamSerializer.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.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 dd07c5ba6a3..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 2e404c7877e..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ResultSerializer.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.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 8e722dcecdc..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 87b1aed30f2..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 98ad62cbb02..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 a1ee1d5bc85..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 15d759aca62..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 59faa57072c..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 59083f45142..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/AbstractWrapSchema.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.codec.protobuf.utils.schema; - -import io.servicecomb.codec.protobuf.utils.WrapSchema; - -import io.protostuff.Schema; - -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 bbb840b28f1..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 e4c3bd0367e..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/ArgsWrapSchema.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.codec.protobuf.utils.schema; - -import java.io.IOException; - -import io.servicecomb.common.javassist.MultiWrapper; - -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Schema; - -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 a20e9c81fd3..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/NormalWrapSchema.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.codec.protobuf.utils.schema; - -import java.io.IOException; - -import io.servicecomb.common.javassist.SingleWrapper; - -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Schema; - -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 9f14a49cd85..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 4b2cfee0166..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/WrapSchemaFactory.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.codec.protobuf.utils.schema; - -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.codec.protobuf.utils.WrapType; - -import io.protostuff.Schema; - -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 83326e68f84..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 ac2ef44a303..00000000000 --- a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtobuf.java +++ /dev/null @@ -1,2858 +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 3c744ef1a92..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,83 +24,82 @@ public class ModelProtostuff { - //CHECKSTYLE:OFF: magicnumber - @Tag(1) - private String destMicroservice; + @Tag(1) + private String destMicroservice; - @Tag(5) - private String schemaId; + @Tag(5) + private String schemaId; - @Tag(6) - private String operationName; + @Tag(6) + private String operationName; - @Tag(7) - private Map context; + @Tag(7) + private Map context; - @Tag(8) - private Map userMap; + @Tag(8) + private Map userMap; - @Tag(9) - private List list = new ArrayList<>(); + @Tag(9) + private List list = new ArrayList<>(); - @Tag(10) - private List userList = new ArrayList<>(); + @Tag(10) + private List userList = new ArrayList<>(); - //CHECKSTYLE:ON + //CHECKSTYLE:ON - public String getDestMicroservice() { - return destMicroservice; - } + public String getDestMicroservice() { + return destMicroservice; + } - public void setDestMicroservice(String destMicroservice) { - this.destMicroservice = destMicroservice; - } + public void setDestMicroservice(String destMicroservice) { + this.destMicroservice = destMicroservice; + } - public String getSchemaId() { - return schemaId; - } + public String getSchemaId() { + return schemaId; + } - public void setSchemaId(String schemaId) { - this.schemaId = schemaId; - } + public void setSchemaId(String schemaId) { + this.schemaId = schemaId; + } - public String getOperationName() { - return operationName; - } + public String getOperationName() { + return operationName; + } - public void setOperationName(String operationName) { - this.operationName = operationName; - } + public void setOperationName(String operationName) { + this.operationName = operationName; + } - public Map getContext() { - return context; - } + public Map getContext() { + return context; + } - public void setContext(Map context) { - this.context = context; - } + public void setContext(Map context) { + this.context = context; + } - public List getList() { - return list; - } + public List getList() { + return list; + } - public void setList(List list) { - this.list = list; - } + public void setList(List list) { + this.list = list; + } - public Map getUserMap() { - return userMap; - } + public Map getUserMap() { + return userMap; + } - public void setUserMap(Map userMap) { - this.userMap = userMap; - } + public void setUserMap(Map userMap) { + this.userMap = userMap; + } - public List getUserList() { - return userList; - } + public List getUserList() { + return userList; + } - public void setUserList(List userList) { - this.userList = userList; - } + public void setUserList(List userList) { + this.userList = userList; + } } 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 38c7458ea0f..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, @@ -16,20 +17,20 @@ package io.protostuff.runtime.model; public class User { - private String name; + private String name; - public User(String name) { - this.name = name; - } + public User(String name) { + this.name = name; + } - public User() { - } + public User() { + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } } 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 cb1fa80b61e..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestAbstractCodec.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.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 0fe6134658a..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 io.servicecomb.codec.protobuf.jackson.CseObjectWriter; -import io.servicecomb.codec.protobuf.jackson.ParamSerializer; -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.ParamDeserializer; - -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 0391f494e5f..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestParamFieldCodec.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 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 aa6f3eb09b1..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestResultFieldCodec.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 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 a602c05b91e..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardParamCodec.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.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; - -import org.junit.Assert; - -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 d3c9c4751ef..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardResultCodec.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.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 27e87d74031..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/definition/TestOperationProtobuf.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.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 8e0303f0a3a..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestAbstractDeserializer.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.codec.protobuf.jackson; - -import java.util.HashMap; -import java.util.Map; - -import io.servicecomb.codec.protobuf.codec.AbstractFieldCodec.ReaderHelpData; -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; - -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 bc4491cebdf..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 io.servicecomb.codec.protobuf.codec.AbstractFieldCodec.ReaderHelpData; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -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 2192558996b..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestParamSerializer.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 32ceb66af8d..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestResultSerializer.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 2d5d83564eb..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestStandardObjectWriter.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.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 39f4c1a2c5b..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/TestProtobufSchemaUtils.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.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.servicecomb.common.javassist.FieldConfig; - -import io.protostuff.ByteArrayInput; -import io.protostuff.Input; -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; - -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 6a9495f1bc0..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsNotWrapSchema.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 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 c93d9addc37..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsWrapSchema.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.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.servicecomb.common.javassist.MultiWrapper; - -import io.protostuff.Input; -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; -import io.protostuff.Schema; - -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 4d4d4339b47..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.1.1-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 cea915f07e6..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/AbstractRestServer.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.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) { - 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.locateOperation(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 f71fffd09e5..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 528cc3e7862..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/RestEngineSchemaListener.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.common.rest; - -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.locator.ServicePathManager; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.loader.SchemaListener; - -@Component -public class RestEngineSchemaListener implements SchemaListener { - private static final Logger LOGGER = LoggerFactory.getLogger(RestEngineSchemaListener.class); - - @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); - - if (mgr.isSchemaExists(schemaMeta.getSchemaId())) { - LOGGER.info("on schema loaded, exists schema. {}:{}", - schemaMeta.getMicroserviceName(), - schemaMeta.getSchemaId()); - continue; - } - LOGGER.info("on schema loaded, new schema. {}:{}", - schemaMeta.getMicroserviceName(), - schemaMeta.getSchemaId()); - mgr.addSchema(schemaMeta.getSchemaId()); - - for (OperationMeta operationMeta : schemaMeta.getOperations()) { - RestOperationMeta restOperationMeta = new RestOperationMeta(); - restOperationMeta.init(operationMeta); - operationMeta.putExtData(RestConst.SWAGGER_REST_OPERATION, restOperationMeta); - mgr.addResource(restOperationMeta); - } - } - - for (ServicePathManager mgr : mgrMap.values()) { - // 对具有动态path operation进行排序 - mgr.sortPath(); - mgr.printService(); - - 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 afdb371b5d6..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 4674eb108fb..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestClientRequest.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; - -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 4b1c94090d5..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 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; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -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 c219c9cbd2f..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 e09a046241f..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 927c703c41b..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 6717615b697..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/AbstractParamProcessor.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.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 0f57469551e..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/BodyProcessorCreator.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.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)) { - return IOUtils.toString(inputStream); - } - 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; - return IOUtils.toString(inputStream); - } - - 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 277d8635c1d..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 3c7e6ce0d9d..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 462952baa08..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 ca5f66f0d47..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 63b63a8cfa8..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 30b4cf23e6d..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessorCreatorManager.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.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 65f8ac39a5a..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/PathProcessorCreator.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.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(value, 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 0a6e5e30a08..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.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.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 fe827c33876..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/RestClientRequestImpl.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.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 f7c4a6e71e9..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 01052e81c02..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.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.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 encodeResponse(OutputStream output, Object result) throws Exception { - RestObjectMapper.INSTANCE.writeValue(output, result); - } - - @Override - public Object decodeResponse(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 426b870ae63..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessor.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.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(); - - void encodeResponse(OutputStream output, Object result) throws Exception; - - default Buffer encodeResponse(Object result) throws Exception { - if (null == result) { - return null; - } - - try (BufferOutputStream output = new BufferOutputStream()) { - encodeResponse(output, result); - return output.getBuffer(); - } - } - - Object decodeResponse(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 decodeResponse(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 301d0ca891e..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessorManager.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.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 475983bb241..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.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.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 encodeResponse(OutputStream output, Object result) throws Exception { - output.write(String.valueOf(result).getBytes(StandardCharsets.UTF_8)); - } - - @Override - public Object decodeResponse(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 eae8ec0d78f..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationComparator.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.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 f2b031c2c64..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationMeta.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 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; -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; - -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(); - } - - setAbsolutePath(concatPath(swagger.getBasePath(), operationMeta.getOperationPath())); - - 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); - } - - this.pathBuilder = new URLPathBuilder(absolutePath, paramMap); - } - - 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); - } - - 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(); - } -} 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 68d2dbc549e..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 e791767b02e..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/PathRegExp.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathRegExp.java deleted file mode 100644 index c939b88dbb9..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathRegExp.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.common.rest.definition.path; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 处理path中的正则表达式 - */ -public class PathRegExp { - // 不带正则表达式的group,使用这个作为正则表达式 - private static final String DEFAULT_REG_EXP = "[^/]+?"; - - private static final byte NAME_READ = 2; - - private static final byte NAME_READ_READY = 3; - - private static final byte NAME_READ_START = 1; - - private static final byte REGEXP_READ = 12; - - private static final byte REGEXP_READ_READY = 13; - - private static final byte REGEXP_READ_START = 11; - - public static final String SLASH = "/"; - - // 静态字符的个数 - protected int staticCharCount; - - // 包括带正则表达式和不带正则表达式的group,总数 - protected int groupCount; - - // 带正则表达式的group数 - protected int groupWithRegExpCount = 0; - - protected final Pattern pattern; - - protected final List varNames = new ArrayList(); - - public static String ensureEndWithSlash(String path) { - if (path.endsWith(SLASH)) { - return path; - } - - return path + "/"; - } - - // 调用者已经保证path不以/打头 - public PathRegExp(String path) - throws Exception { - // a/{id}/{name:.+}/{age}/c变成下面的表达式 - // a/([^/]+?)/(.+)/([^/]+?)/c/(.*) - final int pathLength = path.length(); - final StringBuilder pathPattern = new StringBuilder(); - for (int i = 0; i < pathLength; i++) { - final char c = path.charAt(i); - switch (c) { - case '{': - i = processGroup(path, i, pathPattern); - groupCount++; - break; - case '}': - throw new Exception("'}' is only allowed as " - + "end of a variable name in \"" + path + "\""); - case ';': - throw new Exception("matrix parameters are not allowed in \"" + path - + "\""); - default: - pathPattern.append(c); - staticCharCount++; - } - } - if (pathPattern.length() > 0 - && pathPattern.charAt(pathPattern.length() - 1) != '/') { - pathPattern.append('/'); - } - pathPattern.append("(.*)"); - - pattern = Pattern.compile(pathPattern.toString()); - } - - protected int processGroup(final String path, final int braceIndex, - final StringBuilder pathPattern) throws Exception { - pathPattern.append('('); - final int pathLength = path.length(); - final StringBuilder varName = new StringBuilder(); - final StringBuilder regExp = new StringBuilder(); - int state = NAME_READ_START; - for (int i = braceIndex + 1; i < pathLength; i++) { - final char c = path.charAt(i); - switch (c) { - case '{': - throw new Exception("A variable must not contain an extra '{' in \"" - + path + "\""); - case ' ': - case '\t': - state = processLineBreak(state); - break; - case ':': - state = processColon(path, braceIndex, state); - break; - case '}': - processBrace(path, pathPattern, varName, regExp, state); - return i; - default: - state = processDefault(path, varName, regExp, state, i, c); - break; - } - } - throw new Exception("No '}' found after '{' " + "at position " + braceIndex - + " of \"" + path + "\""); - } - - private int processDefault(final String path, final StringBuilder varName, - final StringBuilder regExp, int state, int i, final char c) throws Exception { - if (state == NAME_READ_START) { - state = NAME_READ; - varName.append(c); - } else if (state == NAME_READ) { - varName.append(c); - } else if (state == REGEXP_READ_START) { - state = REGEXP_READ; - regExp.append(c); - } else if (state == REGEXP_READ) { - regExp.append(c); - } else { - throw new Exception("Invalid character found at position " + i - + " of \"" + path + "\""); - } - return state; - } - - private void processBrace(final String path, final StringBuilder pathPattern, - final StringBuilder varName, final StringBuilder regExp, int state) throws Exception { - if (state == NAME_READ_START) { - throw new Exception( - "The template variable name '{}' is not allowed in " - + "\"" + path + "\""); - } - if ((state == REGEXP_READ) || (state == REGEXP_READ_READY)) { - pathPattern.append(regExp); - if (!regExp.toString().equals(DEFAULT_REG_EXP)) { - groupWithRegExpCount++; - } - } else { - pathPattern.append(DEFAULT_REG_EXP); - } - pathPattern.append(')'); - this.varNames.add(varName.toString()); - } - - private int processColon(final String path, final int braceIndex, int state) throws Exception { - if (state == NAME_READ_START) { - throw new Exception( - "The variable name at position must not be null at " - + braceIndex + " of \"" + path + "\""); - } - if (state == NAME_READ || state == NAME_READ_READY) { - state = REGEXP_READ_START; - } - return state; - } - - private int processLineBreak(int state) { - if (state == NAME_READ) { - state = NAME_READ_READY; - } else if (state == REGEXP_READ) { - state = REGEXP_READ_READY; - } - return state; - } - - // 已知/customers/{id}/address/{id} - // @PathParam("id") String addressId - // url:/customers/123/address/456 - // 则addressId取值为456 - // 即后面的总是覆盖前面的 - public String match(String path, Map varValues) { - Matcher matcher = pattern.matcher(path); - if (!matcher.matches()) { - return null; - } - - for (int i = 1; i < matcher.groupCount(); i++) { - varValues.put(varNames.get(i - 1), matcher.group(i)); - } - - return matcher.group(matcher.groupCount()); - } - - @Override - public String toString() { - return this.pattern.pattern(); - } - - public boolean isStaticPath() { - return groupCount == 0; - } - - public int getStaticCharCount() { - return staticCharCount; - } - - public int getGroupCount() { - return groupCount; - } - - public int getGroupWithRegExpCount() { - return groupWithRegExpCount; - } - -} 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 7ca65cf925b..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 3c95975a2ee..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 ee3cb781e71..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 f722557633e..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 0f56b6e802c..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/OperationGroup.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationGroup.java deleted file mode 100644 index 6308e83c7d9..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationGroup.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.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/OperationLocator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationLocator.java deleted file mode 100644 index 14435b4e191..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationLocator.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.common.rest.locator; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import javax.ws.rs.core.Response.Status; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 从path和http method定位到具体的operation - */ -public class OperationLocator { - private static final Logger LOGGER = LoggerFactory.getLogger(OperationLocator.class); - - private static final String SLASH = "/"; - - protected RestOperationMeta operation; - - protected Map pathVarMap = new HashMap<>(); - - protected boolean resourceFound = false; - - public RestOperationMeta getOperation() { - return this.operation; - } - - public Map getPathVarMap() { - return this.pathVarMap; - } - - // 先在静态路径operation list中查找;如果找不到,则在动态路径operation list中查找 - public void locate(ServicePathManager servicePathManager, String path, String httpMethod) { - // 在静态路径中查找 - operation = locateStaticPathOperation(path, httpMethod, servicePathManager.getStaticPathOperationMap()); - if (operation != null) { - // 全部定位完成 - return; - } - - // 在动态路径中查找 - operation = locateDynamicPathOperation(path, servicePathManager.getDynamicPathOperationList(), httpMethod); - if (operation != null) { - return; - } - - Status status = Status.NOT_FOUND; - if (resourceFound) { - status = Status.METHOD_NOT_ALLOWED; - } - LOGGER.error("locate path failed, status:{}, http method:{}, path:{}, microserviceName:{}", - status, - httpMethod, - path, - servicePathManager.getMicroserviceMeta().getName()); - throw new InvocationException(status, status.getReasonPhrase()); - } - - protected RestOperationMeta locateStaticPathOperation(String path, String httpMethod, - Map staticPathOperations) { - OperationGroup group = staticPathOperations.get(path); - if (group == null) { - return null; - } - - resourceFound = true; - return group.findValue(httpMethod); - } - - protected RestOperationMeta locateDynamicPathOperation(String path, Collection resourceList, - String httpMethod) { - for (RestOperationMeta resource : resourceList) { - String remainPath = resource.getAbsolutePathRegExp().match(path, pathVarMap); - // 刚好匹配,不多也不少 - if ("".equals(remainPath)) { - resourceFound = true; - if (checkHttpMethod(resource, httpMethod)) { - return resource; - } - } - } - return null; - } - - protected boolean checkHttpMethod(RestOperationMeta operation, String httpMethod) { - return operation.getHttpMethod().equals(httpMethod); - } - - // Path: /a/b/c -> /a/b/c/ - static String getStandardPath(String path) { - if (path.length() > 0 && !path.endsWith(SLASH)) { - path += SLASH; - } - return path; - } -} 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 50ff206fb76..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/ServicePathManager.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.common.rest.locator; - -import io.servicecomb.common.rest.definition.RestOperationComparator; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.definition.MicroserviceMeta; -import java.util.ArrayList; -import java.util.Collections; -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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 对静态路径和动态路径的operation进行预先处理,加速operation的查询定位 - */ -public class ServicePathManager { - private static final Logger LOGGER = LoggerFactory.getLogger(ServicePathManager.class); - - private static final String REST_PATH_MANAGER = "RestServicePathManager"; - - protected MicroserviceMeta microserviceMeta; - - // 运行阶段,静态path,一次直接查找到目标,不必遍历查找 - // 以path为key - protected Map staticPathOperations = new HashMap<>(); - - // 运行阶段,以path优先级,从高到低排列的operation列表 - protected List dynamicPathOperationsList = new ArrayList<>(); - - // 已经有哪些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(String schemaId) { - schemaIdSet.add(schemaId); - } - - public ServicePathManager cloneServicePathManager() { - ServicePathManager mgr = new ServicePathManager(microserviceMeta); - mgr.staticPathOperations.putAll(staticPathOperations); - mgr.dynamicPathOperationsList.addAll(dynamicPathOperationsList); - mgr.schemaIdSet.addAll(schemaIdSet); - return mgr; - } - - public OperationLocator locateOperation(String path, String httpMethod) { - String standPath = OperationLocator.getStandardPath(path); - OperationLocator locator = new OperationLocator(); - locator.locate(this, standPath, httpMethod); - - return locator; - } - - 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 RuntimeException( - String.format("operation with url %s, method %s is duplicated", path, httpMethod)); - } - - public Map getStaticPathOperationMap() { - return staticPathOperations; - } - - public List getDynamicPathOperationList() { - return dynamicPathOperationsList; - } - - public void printService() { - if (!LOGGER.isDebugEnabled()) { - return; - } - - doPrintService(); - } - - protected void doPrintService() { - for (Entry entry : staticPathOperations.entrySet()) { - OperationGroup operationGroup = entry.getValue(); - for (RestOperationMeta operation : operationGroup.values()) { - LOGGER.debug(entry.getKey() + " " + operation.getHttpMethod()); - } - } - - for (RestOperationMeta operation : getDynamicPathOperationList()) { - LOGGER.debug(operation.getAbsolutePath() + " " + operation.getHttpMethod()); - } - } -} 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/org/apache/servicecomb/common/rest/definition/path/PathRegExp.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathRegExp.java new file mode 100644 index 00000000000..34f04b57b65 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathRegExp.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.common.rest.definition.path; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 处理path中的正则表达式 + */ +public class PathRegExp { + // 不带正则表达式的group,使用这个作为正则表达式 + private static final String DEFAULT_REG_EXP = "[^/]+?"; + + private static final byte NAME_READ = 2; + + private static final byte NAME_READ_READY = 3; + + private static final byte NAME_READ_START = 1; + + private static final byte REGEXP_READ = 12; + + private static final byte REGEXP_READ_READY = 13; + + private static final byte REGEXP_READ_START = 11; + + public static final String SLASH = "/"; + + // 静态字符的个数 + protected int staticCharCount; + + // 包括带正则表达式和不带正则表达式的group,总数 + protected int groupCount; + + // 带正则表达式的group数 + protected int groupWithRegExpCount = 0; + + protected final Pattern pattern; + + protected final List varNames = new ArrayList<>(); + + public static String ensureEndWithSlash(String path) { + if (path.endsWith(SLASH)) { + return path; + } + + return path + "/"; + } + + // 调用者已经保证path不以/打头 + public PathRegExp(String path) + throws Exception { + // a/{id}/{name:.+}/{age}/c变成下面的表达式 + // a/([^/]+?)/(.+)/([^/]+?)/c/(.*) + final int pathLength = path.length(); + final StringBuilder pathPattern = new StringBuilder(); + for (int i = 0; i < pathLength; i++) { + final char c = path.charAt(i); + switch (c) { + case '{': + i = processGroup(path, i, pathPattern); + groupCount++; + break; + case '}': + throw new Exception("'}' is only allowed as " + + "end of a variable name in \"" + path + "\""); + case ';': + throw new Exception("matrix parameters are not allowed in \"" + path + + "\""); + default: + pathPattern.append(c); + staticCharCount++; + } + } + if (pathPattern.length() > 0 + && pathPattern.charAt(pathPattern.length() - 1) != '/') { + pathPattern.append('/'); + } + pathPattern.append("(.*)"); + + pattern = Pattern.compile(pathPattern.toString()); + } + + protected int processGroup(final String path, final int braceIndex, + final StringBuilder pathPattern) throws Exception { + pathPattern.append('('); + final int pathLength = path.length(); + final StringBuilder varName = new StringBuilder(); + final StringBuilder regExp = new StringBuilder(); + int state = NAME_READ_START; + for (int i = braceIndex + 1; i < pathLength; i++) { + final char c = path.charAt(i); + switch (c) { + case '{': + throw new Exception("A variable must not contain an extra '{' in \"" + + path + "\""); + case ' ': + case '\t': + state = processLineBreak(state); + break; + case ':': + state = processColon(path, braceIndex, state); + break; + case '}': + processBrace(path, pathPattern, varName, regExp, state); + return i; + default: + state = processDefault(path, varName, regExp, state, i, c); + break; + } + } + throw new Exception("No '}' found after '{' " + "at position " + braceIndex + + " of \"" + path + "\""); + } + + private int processDefault(final String path, final StringBuilder varName, + final StringBuilder regExp, int state, int i, final char c) throws Exception { + if (state == NAME_READ_START) { + state = NAME_READ; + varName.append(c); + } else if (state == NAME_READ) { + varName.append(c); + } else if (state == REGEXP_READ_START) { + state = REGEXP_READ; + regExp.append(c); + } else if (state == REGEXP_READ) { + regExp.append(c); + } else { + throw new Exception("Invalid character found at position " + i + + " of \"" + path + "\""); + } + return state; + } + + private void processBrace(final String path, final StringBuilder pathPattern, + final StringBuilder varName, final StringBuilder regExp, int state) throws Exception { + if (state == NAME_READ_START) { + throw new Exception( + "The template variable name '{}' is not allowed in " + + "\"" + path + "\""); + } + if ((state == REGEXP_READ) || (state == REGEXP_READ_READY)) { + pathPattern.append(regExp); + if (!regExp.toString().equals(DEFAULT_REG_EXP)) { + groupWithRegExpCount++; + } + } else { + pathPattern.append(DEFAULT_REG_EXP); + } + pathPattern.append(')'); + this.varNames.add(varName.toString()); + } + + private int processColon(final String path, final int braceIndex, int state) throws Exception { + if (state == NAME_READ_START) { + throw new Exception( + "The variable name at position must not be null at " + + braceIndex + " of \"" + path + "\""); + } + if (state == NAME_READ || state == NAME_READ_READY) { + state = REGEXP_READ_START; + } + return state; + } + + private int processLineBreak(int state) { + if (state == NAME_READ) { + state = NAME_READ_READY; + } else if (state == REGEXP_READ) { + state = REGEXP_READ_READY; + } + return state; + } + + // 已知/customers/{id}/address/{id} + // @PathParam("id") String addressId + // url:/customers/123/address/456 + // 则addressId取值为456 + // 即后面的总是覆盖前面的 + public String match(String path, Map varValues) { + Matcher matcher = pattern.matcher(path); + if (!matcher.matches()) { + return null; + } + + for (int i = 1; i < matcher.groupCount(); i++) { + varValues.put(varNames.get(i - 1), matcher.group(i)); + } + + return matcher.group(matcher.groupCount()); + } + + @Override + public String toString() { + return this.pattern.pattern(); + } + + public boolean isStaticPath() { + return groupCount == 0; + } + + public int getStaticCharCount() { + return staticCharCount; + } + + public int getGroupCount() { + return groupCount; + } + + public int getGroupWithRegExpCount() { + return groupWithRegExpCount; + } +} 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/org/apache/servicecomb/common/rest/locator/OperationLocator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java new file mode 100644 index 00000000000..4b94cee9e36 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.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.common.rest.locator; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.ws.rs.core.Response.Status; + +/** + * 从path和http method定位到具体的operation + */ +public class OperationLocator { + private static final Logger LOGGER = LoggerFactory.getLogger(OperationLocator.class); + + private static final String SLASH = "/"; + + protected RestOperationMeta operation; + + protected Map pathVarMap = new HashMap<>(); + + protected boolean resourceFound = false; + + public RestOperationMeta getOperation() { + return this.operation; + } + + public Map getPathVarMap() { + return this.pathVarMap; + } + + // 先在静态路径operation list中查找;如果找不到,则在动态路径operation list中查找 + public void locate(String microserviceName, String path, String httpMethod, MicroservicePaths microservicePaths) { + // 在静态路径中查找 + operation = locateStaticPathOperation(path, httpMethod, microservicePaths.getStaticPathOperationMap()); + if (operation != null) { + // 全部定位完成 + return; + } + + // 在动态路径中查找 + operation = locateDynamicPathOperation(path, microservicePaths.getDynamicPathOperationList(), httpMethod); + if (operation != null) { + return; + } + + Status status = Status.NOT_FOUND; + if (resourceFound) { + status = Status.METHOD_NOT_ALLOWED; + } + LOGGER.error("locate path failed, status:{}, http method:{}, path:{}, microserviceName:{}", + status, + httpMethod, + path, + microserviceName); + throw new InvocationException(status, status.getReasonPhrase()); + } + + protected RestOperationMeta locateStaticPathOperation(String path, String httpMethod, + Map staticPathOperations) { + OperationGroup group = staticPathOperations.get(path); + if (group == null) { + return null; + } + + resourceFound = true; + return group.findValue(httpMethod); + } + + protected RestOperationMeta locateDynamicPathOperation(String path, Collection resourceList, + String httpMethod) { + for (RestOperationMeta resource : resourceList) { + String remainPath = resource.getAbsolutePathRegExp().match(path, pathVarMap); + // 刚好匹配,不多也不少 + if ("".equals(remainPath)) { + resourceFound = true; + if (checkHttpMethod(resource, httpMethod)) { + return resource; + } + } + } + return null; + } + + protected boolean checkHttpMethod(RestOperationMeta operation, String httpMethod) { + return operation.getHttpMethod().equals(httpMethod); + } + + // 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; + } + return path; + } +} 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 952c5743ac6..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 509209c3826..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestDefPath.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.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 3ee21f657b1..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 577e7610185..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestRestEngineSchemaListener.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; - -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; -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; - -public class TestRestEngineSchemaListener { - private final SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - private static class Impl { - @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, Impl.class); - Swagger swagger = generator.generate(); - 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.assertNotNull(spm.getStaticPathOperationMap().get("/Impl/add/")); - } -} 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 52177ca0a14..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestParam.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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/TestProduce.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestProduce.java deleted file mode 100644 index dbb16381353..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestProduce.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 org.junit.Assert; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.OutputStream; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; -import io.servicecomb.common.rest.codec.produce.ProduceProcessorManager; -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; - -import io.vertx.core.buffer.Buffer; - -public class TestProduce { - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - /** - * Test Produce - * - * @throws Exception - */ - @Test - public void testProduce() throws Exception { - Assert.assertEquals("produce processor mgr", ProduceProcessorManager.INSTANCE.getName()); - Buffer oBuffer = ProduceProcessorManager.DEFAULT_PROCESSOR.encodeResponse("test"); - OutputStream oOutputStream = new BufferOutputStream(); - ProduceProcessorManager.DEFAULT_PROCESSOR.encodeResponse(oOutputStream, "test2"); - JavaType targetType = TypeFactory.defaultInstance().constructType(String.class); - InputStream oInputStream = new ByteArrayInputStream(("true").getBytes()); - ProduceProcessorManager.DEFAULT_PROCESSOR.decodeResponse(oInputStream, targetType); - ProduceProcessorManager.PLAIN_PROCESSOR.encodeResponse(new BufferOutputStream(), "test2"); - Assert.assertNotEquals(null, ProduceProcessorManager.PLAIN_PROCESSOR.decodeResponse(oInputStream, targetType)); - oInputStream = new ByteArrayInputStream(("true").getBytes()); - Assert.assertNotEquals(null, - ProduceProcessorManager.DEFAULT_PROCESSOR.decodeResponse(oInputStream, targetType)); - ProduceProcessorManager.DEFAULT_PROCESSOR.decodeResponse(oBuffer, targetType); - Assert.assertEquals(null, ProduceProcessorManager.DEFAULT_PROCESSOR.encodeResponse(null)); - - } -} 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 c33d52558fb..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestRestCodec.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 io.servicecomb.common.rest.codec.param.ParamValueProcessor; -import io.servicecomb.common.rest.codec.param.RestClientRequestImpl; -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.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 2853b3ec940..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessor.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.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.swagger.generator.core.SwaggerConst; -import io.servicecomb.foundation.vertx.stream.BufferInputStream; - -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 94745f6d2f6..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.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.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 5e2cbe615df..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestRawJsonBodyProcessor.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.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/definition/TestPath.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestPath.java deleted file mode 100644 index b59aafd2d79..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestPath.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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/TestRestOperationMeta.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationMeta.java deleted file mode 100644 index bf45cef6a72..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationMeta.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.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 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; -import javax.ws.rs.core.MediaType; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -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/locator/TestLocator.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestLocator.java deleted file mode 100644 index 5b9053f82c3..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestLocator.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.common.rest.locator; - -import java.util.Collections; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class TestLocator { - @Test - public void testServicePathManager() { - MicroserviceMeta msm = new MicroserviceMeta("app:ms"); - ServicePathManager spm = new ServicePathManager(msm); - - RestOperationMeta rom = createRestOperatonMeta("GET", "abc/{id}"); - spm.addResource(rom); - - rom = createRestOperatonMeta("GET", "abc/{id}/xxx"); - spm.addResource(rom); - - Assert.assertEquals("abc/{id}", spm.getDynamicPathOperationList().get(0).getAbsolutePath()); - spm.sortPath(); - Assert.assertEquals("abc/{id}/xxx", spm.getDynamicPathOperationList().get(0).getAbsolutePath()); - - spm.printService(); - spm.doPrintService(); - } - - @Test - public void testLocateDynamic() { - MicroserviceMeta msm = new MicroserviceMeta("app:ms"); - ServicePathManager spm = new ServicePathManager(msm); - - RestOperationMeta rom = createRestOperatonMeta("GET", "abc/{id}"); - spm.addResource(rom); - - try { - spm.locateOperation("abc/10", "PUT"); - } catch (InvocationException e) { - Assert.assertEquals("Method Not Allowed", ((CommonExceptionData) e.getErrorData()).getMessage()); - } - - OperationLocator locator = spm.locateOperation("abc/10", "GET"); - Assert.assertEquals("10", locator.getPathVarMap().get("id")); - } - - @Test - public void testLocateStatic() { - MicroserviceMeta msm = new MicroserviceMeta("app:ms"); - ServicePathManager spm = new ServicePathManager(msm); - - RestOperationMeta rom = createRestOperatonMeta("GET", "abc/"); - spm.addResource(rom); - - rom = createRestOperatonMeta("POST", "abc/"); - spm.addResource(rom); - - try { - spm.addResource(rom); - } catch (Throwable e) { - Assert.assertEquals("operation with url abc/, method POST is duplicated", e.getMessage()); - } - - Assert.assertEquals(1, spm.getStaticPathOperationMap().size()); - Assert.assertEquals(2, spm.getStaticPathOperationMap().get("abc/").values().size()); - - try { - spm.locateOperation("abcd", "GET"); - } catch (InvocationException e) { - Assert.assertEquals("Not Found", ((CommonExceptionData) e.getErrorData()).getMessage()); - } - - try { - spm.locateOperation("abc/", "PUT"); - } catch (InvocationException e) { - Assert.assertEquals("Method Not Allowed", ((CommonExceptionData) e.getErrorData()).getMessage()); - } - - OperationLocator locator = spm.locateOperation("abc/", "GET"); - Assert.assertEquals(Collections.emptyMap(), locator.getPathVarMap()); - - locator.locate(spm, "abc/", "POST"); - Assert.assertEquals(Collections.emptyMap(), locator.getPathVarMap()); - } - - protected RestOperationMeta createRestOperatonMeta(String httpMethod, String path) { - OperationMeta om = new OperationMeta(); - om.setHttpMethod(httpMethod); - - RestOperationMeta rom = new RestOperationMeta(); - rom.setOperationMeta(om); - rom.setAbsolutePath(path); - return rom; - } - -} 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 88c31b65a53..c39c89ff4ce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 - io.servicecomb - java-chassis - 0.1.1-SNAPSHOT + org.apache.servicecomb + java-chassis-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 5b33c009a5f..af73c36dff9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - java-chassis - 0.1.1-SNAPSHOT - - java-chassis-core - - - io.servicecomb - service-registry - - - io.servicecomb - swagger-invocation-core - - - org.jmockit - jmockit - provided - - - org.mockito - mockito-core - provided - - - junit - junit - provided - - - 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 + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + java-chassis-core + Java Chassis::Core + + + + org.apache.servicecomb + foundation-vertx + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + swagger-invocation-core + + + org.apache.servicecomb + swagger-generator-core + + + io.zipkin.brave + brave + + + org.hibernate.validator + hibernate-validator + + + org.apache.servicecomb + servicecomb-governance + + + org.springframework.boot + spring-boot-autoconfigure + + + io.vertx + vertx-codegen + provided + + + + 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/core/src/main/java/io/servicecomb/core/BootListener.java b/core/src/main/java/io/servicecomb/core/BootListener.java deleted file mode 100644 index 07bfe9f7e9c..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 b68b91456ae..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 24f3213a655..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 121e4e2d086..00000000000 --- a/core/src/main/java/io/servicecomb/core/CseApplicationListener.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.core; - -import java.util.Collection; - -import javax.inject.Inject; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -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.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 { - 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; - - @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 (event instanceof ContextRefreshedEvent) { - ApplicationContext applicationContext = ((ContextRefreshedEvent) event).getApplicationContext(); - //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 { - BeanUtils.setContext(applicationContext); - bootListenerList = applicationContext.getBeansOfType(BootListener.class).values(); - - RegistryUtils.init(); - 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(); - 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 f2fc64cbae5..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 4a5e31c8aa0..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 889f1aef739..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 4eb3a8f36e6..00000000000 --- a/core/src/main/java/io/servicecomb/core/Invocation.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; - -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 b9ed900e5c6..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 125c3f8ecbe..00000000000 --- a/core/src/main/java/io/servicecomb/core/Transport.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; - -import io.servicecomb.swagger.invocation.AsyncResponse; - -// TODO:感觉要拆成显式的client、server才好些 -public interface Transport { - String getName(); - - 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 d0dcca8db34..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 00de4d6cd76..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 353a48c3e6e..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.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.foundation.common.RegisterManager; - -/** - * 微服务名为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 faa093cb38d..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 b30bdc8d844..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/OperationMeta.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 0abc50f717c..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/SchemaMeta.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.core.definition; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map.Entry; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.exception.ExceptionUtils; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.servicecomb.foundation.common.utils.ReflectUtils; - -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 { - // 如果要生成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); - } - - Method method = ReflectUtils.findMethod(swaggerIntf, operation.getOperationId()); - if (method == null) { - throw ExceptionUtils.operationNotExist(getSchemaId(), operation.getOperationId()); - } - - 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 e09c4f2e058..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 24c2ba42bd8..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 io.servicecomb.core.CseContext; -import io.servicecomb.core.definition.SchemaMeta; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; - -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.foundation.common.config.PaaSResourceUtils; - -/** - * 场景: - * 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 4d314e44c49..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 a5a7faf70a4..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaListenerManager.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.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 io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import org.springframework.stereotype.Component; - -/** - * 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 3efd6bc5a42..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaLoader.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.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 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.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; - } - - 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 2eecfa6da8c..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 1d41c96c7fc..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 f76a1174503..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/schema/ConsumerSchemaFactory.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.core.definition.schema; - -import java.util.HashSet; -import java.util.Set; - -import javax.inject.Inject; - -import org.apache.commons.io.FilenameUtils; -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.definition.MicroserviceMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.SchemaUtils; -import io.servicecomb.core.definition.loader.SchemaListenerManager; -import io.servicecomb.foundation.common.config.PaaSResourceUtils; -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); - Set schemaIds = findLocalSchemas(microserviceMeta); - Microservice microservice = - findMicroservice(microserviceMeta, microserviceVersionRule); - if (microservice != null) { - schemaIds.addAll(microservice.getSchemas()); - } - - getOrCreateConsumerSchema(microserviceMeta, schemaIds, microservice); - - microserviceMetaManager.register(microserviceName, microserviceMeta); - schemaListenerManager.notifySchemaListener(microserviceMeta); - return microserviceMeta; - } - - } - - protected Set findLocalSchemas(MicroserviceMeta microserviceMeta) { - String resPath = generateSchemaPath(microserviceMeta.getName(), "*"); - Resource[] resArr = PaaSResourceUtils.getResources("classpath*:" + resPath); - - Set schemaIds = new HashSet<>(); - for (Resource res : resArr) { - String schemaId = FilenameUtils.getBaseName(res.getFilename()); - schemaIds.add(schemaId); - } - - LOGGER.info("Found schema ids local, {}:{}:{}", - microserviceMeta.getAppId(), - microserviceMeta.getName(), - schemaIds); - return schemaIds; - } - - 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,表示原型与契约相同 - // 如果从远程获取microservice失败,则microservice为null - protected void getOrCreateConsumerSchema(MicroserviceMeta microserviceMeta, Set schemaIds, - Microservice microservice) { - for (String schemaId : schemaIds) { - 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; - } - - if (context.getMicroservice() == null) { - throw new Error( - String.format("no schema in local, and can not get microservice from service center, %s:%s", - context.getMicroserviceName(), - context.getSchemaId())); - } - - 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 38541b8d225..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 d69ee18faf9..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 0297933f0a8..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 ce43c5a103c..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 d9c558efc34..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 18f0a1799ad..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 8a2fc5572ff..00000000000 --- a/core/src/main/java/io/servicecomb/core/exception/ExceptionUtils.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.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_SCHEMA_OPERATION_NOT_EXIST = "cse.schema.operation.not.exist"; - - 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_SCHEMA_OPERATION_NOT_EXIST, - "Operation not exist in interface, schemaId=%s, operation=%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 operationNotExist(String schemaId, String operationName) { - return createCseException(CSE_SCHEMA_OPERATION_NOT_EXIST, - schemaId, - operationName); - } - - 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 1824a25b683..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 io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.common.utils.BeanUtils; -import com.netflix.config.DynamicPropertyFactory; - -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 8f9005f1dbe..00000000000 --- a/core/src/main/java/io/servicecomb/core/executor/FixedThreadExecutor.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.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; - -public class FixedThreadExecutor implements Executor { - private List executorList = new ArrayList<>(); - - private AtomicInteger index = new AtomicInteger(); - - private Map threadExectorMap = new ConcurrentHashMap<>(); - - public FixedThreadExecutor() { - executorList.add(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); - executorList.add(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); - } - - @Override - public void execute(Runnable command) { - long threadId = Thread.currentThread().getId(); - Executor executor = threadExectorMap.get(threadId); - if (executor == null) { - int idx = index.getAndIncrement() % executorList.size(); - executor = executorList.get(idx); - threadExectorMap.put(threadId, executor); - } - - executor.execute(command); - } -} 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 497736ecd2c..00000000000 --- a/core/src/main/java/io/servicecomb/core/executor/ReactiveExecutor.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.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 fa36834cce4..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/AbstractHandlerManager.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.core.handler; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.springframework.util.StringUtils; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.handler.config.Config; -import io.servicecomb.foundation.common.AbstractObjectManager; -import com.netflix.config.DynamicPropertyFactory; - -// 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 e16794a1e25..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 75e4dcefab0..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 70cd7666a3b..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 155238a8726..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/ShutdownHookHandler.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.core.handler; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 实现调用链的优雅停止: 当调用链没有返回的时候,等待返回或者超时 - */ -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 4437abd5a9f..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/config/Config.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.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 fef27e8820f..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/config/HandlerConfig.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 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 fbc0781e33e..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 585c3a46959..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/impl/ProducerOperationHandler.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.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 ab578592cf0..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/impl/SimpleLoadBalanceHandler.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.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 47af5297562..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/impl/TransportClientHandler.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.handler.impl; - -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.AsyncResponse; - -public class TransportClientHandler extends AbstractHandler { - public static final TransportClientHandler INSTANCE = new TransportClientHandler(); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - invocation.getTransport().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 0df34559329..00000000000 --- a/core/src/main/java/io/servicecomb/core/invocation/InvocationFactory.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.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 static String microserviceName = getMicroserviceName(); - - private InvocationFactory() { - } - - private static String getMicroserviceName() { - return RegistryUtils.getMicroservice().getServiceName(); - } - - public static void setMicroserviceName(String microserviceName) { - InvocationFactory.microserviceName = microserviceName; - } - - public static Invocation forConsumer(ReferenceConfig referenceConfig, OperationMeta operationMeta, - Object[] swaggerArguments) { - Invocation invocation = new Invocation(referenceConfig, - operationMeta, - swaggerArguments); - invocation.addContext(Const.SRC_MICROSERVICE, microserviceName); - 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, microserviceName); - 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, microserviceName); - 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 5906e40c44c..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 bf1466b3c5a..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 8712024d6e1..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/ConsumerProviderManager.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.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 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 bcd6910b26e..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/InvokerUtils.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.core.provider.consumer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -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.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 = - CseContext.getInstance().getConsumerProviderManager().getReferenceConfig(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 = new ReferenceConfig(CseContext.getInstance().getConsumerSchemaFactory(), - 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 de5fb207ccd..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 a94349f7a03..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/SyncResponseExecutor.java b/core/src/main/java/io/servicecomb/core/provider/consumer/SyncResponseExecutor.java deleted file mode 100644 index aea6dd3a615..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/SyncResponseExecutor.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.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; - 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 9fc4730a4dd..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 28adc36f3dd..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 7c4e77c7ee0..00000000000 --- a/core/src/main/java/io/servicecomb/core/transport/AbstractTransport.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.transport; - -import java.util.Map; -import java.util.Map.Entry; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.foundation.common.net.NetUtils; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.VertxUtils; -import com.netflix.config.DynamicPropertyFactory; - -import io.vertx.core.Vertx; - -public abstract class AbstractTransport implements Transport { - /** - * 用于参数传递:比如向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() throws Exception { - return endpoint; - } - - protected void setListenAddressWithoutSchema(String addressWithoutSchema) { - setListenAddressWithoutSchema(addressWithoutSchema, null); - } - - /** - * 将配置的URI转换为endpoint - * addressWithoutSchema 配置的URI,没有schema部分 - */ - protected void setListenAddressWithoutSchema(String addressWithoutSchema, - Map pairs) { - if (addressWithoutSchema != null && pairs != null && !pairs.isEmpty()) { - int idx = addressWithoutSchema.indexOf('?'); - if (idx == -1) { - addressWithoutSchema += "?"; - } else { - addressWithoutSchema += "&"; - } - - StringBuilder sb = new StringBuilder(); - for (Entry entry : pairs.entrySet()) { - sb.append(entry.getKey()).append('=').append(entry.getValue()).append('&'); - } - sb.setLength(sb.length() - 1); - addressWithoutSchema += sb.toString(); - } - 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; - } - - } - - @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 988d8fc55b2..00000000000 --- a/core/src/main/java/io/servicecomb/core/transport/TransportManager.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.core.transport; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -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.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 transportList; - - private Map transportMap = new HashMap<>(); - - public void init() throws Exception { - for (Transport transport : transportList) { - transportMap.put(transport.getName(), transport); - - 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; - } - } - } - - 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 e349566a6a1..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 fa901133adf..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 90c1a22b0f4..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 5f63b29982f..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestConsumer.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; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; - -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.core.provider.consumer.SyncResponseExecutor; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.definition.SchemaMeta; -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 f337bb7e405..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestCseApplicationListener.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; - -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() { - { - event.getApplicationContext(); - result = context; - context.getBeansOfType(BootListener.class); - result = listeners; - } - }; - - CseApplicationListener cal = new CseApplicationListener(); - 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) throws Exception { - Map listeners = new HashMap<>(); - listeners.put("test", listener); - - new Expectations() { - { - event.getApplicationContext(); - result = context; - context.getBeansOfType(BootListener.class); - result = listeners; - } - }; - CseApplicationListener cal = new CseApplicationListener(); - ReflectUtils.setField(cal, "producerProviderManager", producerProviderManager); - cal.onApplicationEvent(event); - } - - @Test - public void testCseApplicationListenerParentNotnull(@Injectable ContextRefreshedEvent event, - @Injectable AbstractApplicationContext context, - @Injectable AbstractApplicationContext pContext) throws Exception { - - new Expectations() { - { - event.getApplicationContext(); - result = context; - } - }; - CseApplicationListener cal = new CseApplicationListener(); - 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 365fb64bede..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 5e80814e68a..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 ca20821d7a6..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestException.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; - -import io.servicecomb.core.exception.CseException; -import org.junit.Assert; -import org.junit.Test; - -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.operationNotExist("cse.double.error", "what path are you talking about"); - Assert.assertEquals("cse.schema.operation.not.exist", 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 889175cc512..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestExecutors.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.core; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - -import io.servicecomb.core.executor.ReactiveExecutor; -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.executor.FixedThreadExecutor; - -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 96155a4cbea..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestHandler.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.core; - -import io.servicecomb.core.handler.ConsumerHandlerManager; -import org.junit.Assert; -import org.junit.Test; - -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 2bcab5b0404..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 294f040ca99..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestTransport.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; - -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 5e68a1f6cd4..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/TestInvokerUtils.java b/core/src/test/java/io/servicecomb/core/consumer/TestInvokerUtils.java deleted file mode 100644 index 7289ec34ea4..00000000000 --- a/core/src/test/java/io/servicecomb/core/consumer/TestInvokerUtils.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.consumer; - -import org.junit.Test; - -import io.servicecomb.core.provider.consumer.InvokerUtils; -import mockit.Mocked; - -public class TestInvokerUtils { - @Test - public void testInvokerUtils(@Mocked io.servicecomb.core.provider.consumer.InvokerUtils util) { - // just test for interface compatible - InvokerUtils.syncInvoke((String) null, (String) null, (String) null, (Object[]) null); - InvokerUtils.syncInvoke((String) null, - (String) null, - (String) null, - (String) null, - (String) null, - (Object[]) null); - } -} 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 d4f0a8806f6..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 io.servicecomb.core.provider.consumer.ReactiveResponseExecutor; -import org.junit.Assert; -import org.junit.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; - } - 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 93909f298ac..00000000000 --- a/core/src/test/java/io/servicecomb/core/consumer/TestSyncResponseExecutor.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.consumer; - -import io.servicecomb.core.provider.consumer.SyncResponseExecutor; -import io.servicecomb.swagger.invocation.Response; - -import org.junit.Assert; -import org.junit.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); - 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 7b4ec2a23d0..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/MicroServicePropertyExtendedStub.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.core.definition; - -import java.util.Map; - -import io.servicecomb.serviceregistry.api.PropertyExtended; - -/** - * - * @since Mar 7, 2017 - * @see - */ -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 d5c9fe57499..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 6e9256cb565..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 008fa40d203..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/TestOperationMeta.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.core.definition; - -import io.servicecomb.core.unittest.UnitTestMeta; -import org.junit.Assert; -import org.junit.Test; - -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/TestSchemaUtils.java b/core/src/test/java/io/servicecomb/core/definition/TestSchemaUtils.java deleted file mode 100644 index 4a29efb0f7c..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 b65c7006471..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/loader/TestDynamicSchemaLoader.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.Collections; - -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.registry.ServiceRegistryFactory; - -public class TestDynamicSchemaLoader { - private static MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - - @BeforeClass - public static void init() { - UnitTestMeta.init(); - - SchemaLoader loader = new SchemaLoader(); - 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(); - - 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()); - } -} 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 086d11fe165..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/loader/TestSchemaListenerManager.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.definition.loader; - -import java.util.Arrays; - -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -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 11ff62cc647..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/schema/TestConsumerSchemaFactory.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.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 325de4c0466..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/schema/TestProducerSchemaFactory.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.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 2f4b2ec6b24..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 3f3e510b6ac..00000000000 --- a/core/src/test/java/io/servicecomb/core/handler/impl/TestServiceProviderHandler.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.core.handler.impl; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.AsyncResponse; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -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 3fdaee87df4..00000000000 --- a/core/src/test/java/io/servicecomb/core/handler/impl/TestSimpleLoadBalanceHandler.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.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 81ac4156478..00000000000 --- a/core/src/test/java/io/servicecomb/core/handler/impl/TestTransportClientHandler.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 io.servicecomb.core.Invocation; -import io.servicecomb.core.Transport; -import io.servicecomb.swagger.invocation.AsyncResponse; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -public class TestTransportClientHandler { - TransportClientHandler transportClientHandler; - - Invocation invocation = null; - - AsyncResponse asyncResp = null; - - Transport transport = null; - - @Before - public void setUp() throws Exception { - transportClientHandler = TransportClientHandler.INSTANCE; - invocation = Mockito.mock(Invocation.class); - asyncResp = Mockito.mock(AsyncResponse.class); - transport = Mockito.mock(Transport.class); - } - - @After - public void tearDown() throws Exception { - transportClientHandler = null; - invocation = null; - asyncResp = null; - transport = null; - } - - @Test - public void test() { - boolean status = false; - try { - Assert.assertNotNull(transport); - Mockito.when(invocation.getTransport()).thenReturn(transport); - transportClientHandler.handle(invocation, asyncResp); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } - -} 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 4d41d9d1be8..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 e20f7ff0968..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 71b1e2e9e36..00000000000 --- a/core/src/test/java/io/servicecomb/core/provider/consumer/TestInvokerUtils.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.core.provider.consumer; - -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.definition.OperationMeta; - -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); - } -} 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 547eddd3776..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 b1fc3070772..00000000000 --- a/core/src/test/java/io/servicecomb/core/transport/TestAbstractTransport.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.transport; - -import io.servicecomb.core.Invocation; -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -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 { - } - } - - @Test - public void testMyAbstractTransport() throws Exception { - MyAbstractTransport transport = new MyAbstractTransport(); - transport.setListenAddressWithoutSchema("127.0.0.1:9090"); - Assert.assertEquals(transport.getName(), "my"); - Assert.assertEquals(transport.getEndpoint().getEndpoint(), "my://127.0.0.1:9090"); - Assert.assertEquals(((IpPort) transport.parseAddress("my://127.0.0.1:9090")).getHostOrIp(), "127.0.0.1"); - transport.setListenAddressWithoutSchema("0.0.0.0:9090"); - Assert.assertNotEquals(transport.getEndpoint().getEndpoint(), "my://127.0.0.1:9090"); - transport.setListenAddressWithoutSchema(null); - Assert.assertEquals(transport.getEndpoint().getEndpoint(), null); - Assert.assertEquals(transport.parseAddress(null), null); - Assert.assertEquals(AbstractTransport.getRequestTimeout(), 30000); - } - - @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 c38827cd32d..00000000000 --- a/core/src/test/java/io/servicecomb/core/transport/TestTransportManager.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.transport; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import mockit.Deencapsulation; -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; - } - }; - List transports = new ArrayList<>(); - transports.add(transport); - - TransportManager manager = new TransportManager(); - Deencapsulation.setField(manager, "transportList", 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.init(); - result = true; - transport.getPublishEndpoint(); - result = endpoint; - } - }; - List transports = new ArrayList<>(); - transports.add(transport); - - TransportManager manager = new TransportManager(); - Deencapsulation.setField(manager, "transportList", transports); - - manager.init(); - Assert.assertEquals(manager.findTransport("test"), transport); - } -} 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 42a1abd8760..97f795fcbf5 100644 --- a/coverage-reports/pom.xml +++ b/coverage-reports/pom.xml @@ -1,12 +1,13 @@ - - java-chassis - io.servicecomb - 0.1.1-SNAPSHOT + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default 4.0.0 @@ -29,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.1.1-SNAPSHOT + org.apache.servicecomb.demo + business-1-0-0 + ${project.version} + test - io.servicecomb.demo - pojo-server - 0.1.1-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.1.1-SNAPSHOT + ${project.version} + test - io.servicecomb.demo + org.apache.servicecomb.demo jaxrs-server - 0.1.1-SNAPSHOT + ${project.version} + test + - io.servicecomb.demo - springmvc-client - 0.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-local-registry-client + ${project.version} + test - io.servicecomb.demo - springmvc-server - 0.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-local-registry-server + ${project.version} + test + - io.servicecomb.demo - crossapp-client - 0.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-multi-registries-client + ${project.version} + test - io.servicecomb.demo - crossapp-server - 0.1.1-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.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-register-url-prefix-client + ${project.version} + test - io.servicecomb.demo - demo-spring-boot-jaxrs-client - 0.1.1-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.1.1-SNAPSHOT + ${project.version} + test - io.servicecomb.demo + org.apache.servicecomb.demo demo-spring-boot-pojo-server - 0.1.1-SNAPSHOT + ${project.version} + test - io.servicecomb.demo - demo-spring-boot-discovery-server - 0.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-spring-boot-springmvc-client + ${project.version} + test - io.servicecomb.demo - demo-spring-boot-discovery-client - 0.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-spring-boot-springmvc-server + ${project.version} + test + - io.servicecomb.demo - demo-spring-boot-zuul-proxy - 0.1.1-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.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-zeroconfig-registry-client + ${project.version} + test - io.servicecomb.tests - springmvc-tests - 0.1.1-SNAPSHOT + org.apache.servicecomb.demo + demo-zeroconfig-registry-server + ${project.version} + test - io.servicecomb.tests - tracing-tests - 0.1.1-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 098c321371d..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.1.1-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.1.1-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 ce480b671e3..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 ef05fa37fba..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 91e9ce134b6..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 d733b883779..00000000000 --- a/demo/demo-crossapp/crossapp-client/src/test/java/io/servicecomb/demo/crossapp/CrossAppIT.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.servicecomb.demo.crossapp; - -import io.servicecomb.demo.TestMgr; -import org.junit.Before; -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -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 c76e850f3b5..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.1.1-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.1.1-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 438028d561f..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 9e08c76fe17..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/CrossappServer.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.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 91e9ce134b6..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 d95430089eb..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/HelloWorldImpl.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.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 29591dd5abe..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.1.1-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 cc0d50a384e..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.1.1-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 789a6e14013..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 9e674497e12..00000000000 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/io/servicecomb/demo/jaxrs/client/JaxrsClient.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.provider.springmvc.reference.RestTemplateBuilder; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -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 f47d51795e4..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.1.1-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.1.1-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 dddc7b6fa57..00000000000 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 4c411de838e..00000000000 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/ComputeImpl.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.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 239614f804e..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 2a12df41a3f..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.1.1-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 c4b3b6b0ce6..00000000000 --- a/demo/demo-local/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.1.1-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 8ef899535f4..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 319483ec8a0..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.1.1-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 5b59e7d26f0..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 453783d35ea..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 933acab69fa..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 8f285c5050c..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.1.1-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 9d6e5d2b3a2..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 c708fa8287e..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 a2fdd9da5e4..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.1.1-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 5f8dc59751d..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 1b12b432334..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 eb4be341627..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 327dae8cad4..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.1.1-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 f1544f267bb..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 39b2dbf664c..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 a82c2bb2444..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.1.1-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.1.1-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 bcbab312695..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 330f3253dea..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.1.1-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.1.1-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 d14ffed0f7f..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 16b0194dbdd..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.1.1-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.1.1-SNAPSHOT - - - io.servicecomb.demo - a-client - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - b-server - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - b-client - 0.1.1-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 19a15f7425d..00000000000 --- a/demo/demo-perf-client/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.1.1-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 503bdff7d4f..00000000000 --- a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/ClientThread.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.client.perf; - -import io.servicecomb.core.CseContext; -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; - CseContext.getInstance().getConsumerProviderManager().setTransport("pojo", Config.getTransport()); - - System.out.printf("test %s performance\n", Config.getTransport()); - - 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 255577d4bb6..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 88428db0fc3..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 e3a5e76a9f8..00000000000 --- a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/PerfClient.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.demo.client.perf; - -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()); - - 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 72e98a99077..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.1.1-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.1.1-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/CodeFirstPojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/CodeFirstPojoClient.java deleted file mode 100644 index 6aa655e9978..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 39b8fe3af9e..00000000000 --- a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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; - - @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 db5f6fa7454..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 2bf5bef357a..00000000000 --- a/demo/demo-pojo/pojo-client/src/test/java/io/servicecomb/demo/pojo/PojoIT.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.servicecomb.demo.pojo; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.pojo.client.PojoClient; -import org.junit.Before; -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -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 bb23354912b..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.1.1-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.1.1-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 01d3c16c3b4..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/CodeFirstPojo.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.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 757afc89ef5..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/HelloImpl.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.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 191b85b5545..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 cda26bcb814..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/SmartCareImpl.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.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 1d2d3d9db00..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 5a7c00d9362..00000000000 --- a/demo/demo-pojo/pojo-tests/pom.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - demo-pojo - io.servicecomb.demo - 0.1.1-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 0af7001d284..00000000000 --- a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/PojoReferenceIntegrationTest.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.integration; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import com.seanyinx.github.unit.scaffolding.Randomness; -import org.junit.BeforeClass; -import org.junit.Test; - -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 044296eeaaa..00000000000 --- a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoService.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.demo.integration; - -import io.servicecomb.demo.CodeFirstPojoIntf; -import io.servicecomb.provider.pojo.RpcReference; -import org.springframework.stereotype.Component; - -@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 11f22f791a3..00000000000 --- a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoTestMain.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.integration; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import javax.inject.Inject; -import org.springframework.stereotype.Component; - -@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 ec309926348..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.1.1-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 bc4c3ceaa3f..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.1.1-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 - - - 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 84db9401d24..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 825f184e797..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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 5541d779d6e..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 03f04610140..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/DemoSSLCustom.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; - -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 fa904fa2300..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 5802093b0bd..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/compute/Compute.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.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 b8a1d98635c..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 486eab041cf..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 4c3c840c27b..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 02e0b3fad9c..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 5e1a6ff0908..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 e7e7bc150a8..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 75865f4c2c1..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/TestResponse.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.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 1f499d437d6..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 4beb641e6d8..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 94ce4f5b86a..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 0d7fa3f4211..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 7c29bd6be6b..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/SmartCare.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.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 a9127adba7e..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 90710b7a6bf..00000000000 --- a/demo/demo-server-servlet/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.1.1-SNAPSHOT - - demo-server-servlet - war - - - - io.servicecomb - transport-rest-servlet - - - - io.servicecomb.demo - pojo-server - - - io.servicecomb - transport-rest-vertx - - - - - - - pojo - - 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 a74d0856180..00000000000 --- a/demo/demo-server-servlet/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - contextConfigLocation - - classpath*:META-INF/spring/*.bean.xml - classpath*:app-config.xml - - - - - io.servicecomb.transport.rest.servlet.RestServletContextListener - - - RestServlet - io.servicecomb.transport.rest.servlet.RestServlet - 1 - true - - - RestServlet - /rest/* - - 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 51e3f37d65a..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.1.1-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.1.1-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 7152b634def..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 0878bb4a9d2..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.1.1-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.1.1-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 9c97748833d..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 54dfea2d955..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,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.demo.discovery.server; - -import io.servicecomb.provider.rest.common.RestSchema; -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; - -@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.yaml b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/application.yaml deleted file mode 100644 index b4e88586711..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: 0 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 c2a12259b80..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/microservice.yaml +++ /dev/null @@ -1,14 +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 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 bbdb1edc9aa..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.1.1-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.1.1-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 bb698ecb88a..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,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.demo.discovery.zuul; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; -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; - -@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.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 4b5dd568878..00000000000 --- a/demo/demo-spring-boot-discovery/pom.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.1.1-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 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-test - test - - - 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 6aefecfed4e..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.1.1-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.1.1-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 40eb59108bd..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,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.jaxrs.client; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.springboot.starter.provider.EnableServiceComb; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * - * - * - */ -@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 99e9441c166..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/test/java/io/servicecomb/springboot/jaxrs/SpringBootJaxrsIT.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.servicecomb.springboot.jaxrs; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.springboot.jaxrs.client.JaxrsClient; -import org.junit.Before; -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -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 bf907d0ee2f..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.1.1-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.1.1-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 ae8752490d1..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,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.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 ecb022df2c7..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.1.1-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 704ccfeed4b..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,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.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 ede488aa9fc..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.1.1-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 12b49d7d89a..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,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.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 4ceb5abb47d..00000000000 --- a/demo/demo-spring-boot-provider/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.1.1-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 2a47031b4a7..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.1.1-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.1.1-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 6c530c8dc67..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 fa904fa2300..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 2e48d2760b7..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,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.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 5e1a6ff0908..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 e7e7bc150a8..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 75865f4c2c1..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/TestResponse.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.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 1f499d437d6..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 ba0f2b3899c..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,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.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 8e715fc0b9f..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 index 7a73057f7e5..1d901a66c99 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml @@ -1,2 +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: 8999 \ No newline at end of file + 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 349bb561b5d..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 673a4f8954e..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.1.1-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.1.1-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 02e0b3fad9c..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 89ffca2b4c1..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 1d2d3d9db00..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 5e1a6ff0908..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 e7e7bc150a8..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 75865f4c2c1..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/TestResponse.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.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 1f499d437d6..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 1f253da2a96..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,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.pojo.server; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import com.netflix.config.DynamicPropertyFactory; - -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); - String port = DynamicPropertyFactory.getInstance().getStringProperty("server.port", null).get(); - System.out.println("port ----------->" + port); - String name = DynamicPropertyFactory.getInstance().getStringProperty("cse.servlet.name", null).get(); - System.out.println("profiles ----------->" + name); - } -} 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 index 960aa9d98b9..e7c0ccf1da0 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/application.yml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/application.yml @@ -1,6 +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 -cse: - servlet: - name: RestServlet - url: /pojo/rest/* \ No newline at end of file + +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 705a8f3109c..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.1.1-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-transport - - - io.servicecomb - spring-boot-starter-provider - - - io.servicecomb - transport-rest-vertx - - - - - 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 d466415b464..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.1.1-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 776660d454f..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.1.1-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.1.1-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 53acb189004..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 b6bab24123a..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/SpringmvcClient.java b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/SpringmvcClient.java deleted file mode 100644 index ea7edaa3aa3..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/SpringmvcClient.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.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")); - - 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")); - - 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 c9580b13a6c..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 011534b8e61..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,24 +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 - + 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 @@ -31,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 b6b78e0d94f..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/test/java/io/servicecomb/demo/springmvc/SpringMvcIT.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.servicecomb.demo.springmvc; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.springmvc.client.SpringmvcClient; -import org.junit.Before; -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -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)); - } -} \ No newline at end of file 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 0362e3bfcd6..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.1.1-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.1.1-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 c8b381ea2ef..00000000000 --- a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/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.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 104f4e25564..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 io.servicecomb.demo.controller.Person; -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.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 8ae37011c29..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 4464454dbf4..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,24 +1,87 @@ -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 -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 + datacenter: + 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 @@ -31,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 b6935de1834..00000000000 --- a/demo/docker-build-config/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - demo-parent - io.servicecomb.demo - 0.1.1-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 d51e7afa910..00000000000 --- a/demo/docker-run-config/pom.xml +++ /dev/null @@ -1,171 +0,0 @@ - - - - demo-parent - io.servicecomb.demo - 0.1.1-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 d26f91fd31e..d0376883c6b 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb - java-chassis - 0.1.1-SNAPSHOT - - 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.1.1-SNAPSHOT - - - io.servicecomb.demo - all-client - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - pojo-server - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - jaxrs-server - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - springmvc-server - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - crossapp-server - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - multiple-server - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - multiple-client - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - pojo-client - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - jaxrs-client - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - springmvc-client - 0.1.1-SNAPSHOT - - - io.servicecomb.demo - crossapp-client - 0.1.1-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} - - - - - - - - - - - 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 - - - - \ No newline at end of file + + + 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/etc/intellij-java-google-style.xml b/etc/intellij-java-google-style.xml index 02b0f0220f7..172c9de5ff0 100644 --- a/etc/intellij-java-google-style.xml +++ b/etc/intellij-java-google-style.xml @@ -29,10 +29,21 @@ + + + + + + + + + +