diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..bb5d2057253 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Git will understand that all files specified are not text, +# and it should not try to change them. +# This will prevent file formatting (such as `crlf` endings to `lf` endings) while commit. +* -text \ No newline at end of file diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 00000000000..50088d255e8 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,185 @@ +name: Build and Test + +on: [push, pull_request, workflow_dispatch] + +env: + FORK_COUNT: 2 + FAIL_FAST: 0 + SHOW_ERROR_DETAIL: 1 + #multi-version size limit + VERSIONS_LIMIT: 4 + CANDIDATE_VERSIONS: ' + spring.version:4.3.30.RELEASE; + spring-boot.version:1.5.22.RELEASE; + spring-boot.version:2.4.1; + ' + DUBBO_SPRING_BOOT_REF: '2.7.x' + +jobs: + build-source: + runs-on: ubuntu-18.04 + outputs: + version: ${{ steps.dubbo-version.outputs.version }} + steps: + - uses: actions/checkout@v2 + with: + path: dubbo + - uses: actions/setup-java@v1 + with: + java-version: 8 + - uses: actions/cache@v2 + name: "Cache local Maven repository" + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + - name: "Dubbo cache" + uses: actions/cache@v2 + with: + path: ~/.m2/repository/org/apache/dubbo + key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} + - name: "Calculate Dubbo Version" + id: dubbo-version + run: | + REVISION=`awk '/[^<]+<\/revision>/{gsub(/|<\/revision>/,"",$1);print $1;exit;}' ./dubbo/pom.xml` + echo "::set-output name=version::$REVISION" + echo "dubbo version: $REVISION" + - name: "Build Dubbo with Maven" + run: | + cd ./dubbo + ./mvnw --batch-mode -U -e --no-transfer-progress clean source:jar install -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true + + unit-test: + needs: [build-source] + name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }})" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ ubuntu-18.04, windows-2019 ] + jdk: [ 8, 11 ] + steps: + - uses: actions/checkout@v2 + - name: "Set up JDK ${{ matrix.jdk }}" + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.jdk }} + - uses: actions/cache@v2 + name: "Cache local Maven repository" + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: "Test with Maven with Integration Tests" + timeout-minutes: 40 + if: ${{ startsWith( matrix.os, 'ubuntu') }} + run: ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true + - name: "Test with Maven without Integration Tests" + env: + DISABLE_FILE_SYSTEM_TEST: true + timeout-minutes: 40 + if: ${{ startsWith( matrix.os, 'windows') }} + run: ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true" + - name: "Pack rat file if failure" + if: failure() + run: 7z a ${{ github.workspace }}/rat.zip *rat.txt -r + - name: "Upload rat file if failure" + if: failure() + uses: actions/upload-artifact@v2 + with: + name: "rat-file-${{ matrix.os }}-JDK${{ matrix.jdk }}" + path: ${{ github.workspace }}/rat.zip + - name: "Upload coverage to Codecov" + uses: codecov/codecov-action@v1 + + integration-test-prepare: + runs-on: ubuntu-18.04 + env: + JOB_COUNT: 3 + steps: + - uses: actions/checkout@v2 + with: + repository: 'apache/dubbo-samples' + ref: master + - name: "Prepare test list" + run: | + bash ./test/scripts/prepare-test.sh + - name: "Upload test list" + uses: actions/upload-artifact@v2 + with: + name: test-list + path: test/jobs + + integration-test-job: + needs: [build-source, integration-test-prepare] + name: "Integration Test on ubuntu-18.04 (JobId: ${{matrix.job_id}})" + runs-on: ubuntu-18.04 + timeout-minutes: 30 + env: + JAVA_VER: 8 + TEST_CASE_FILE: jobs/testjob_${{matrix.job_id}}.txt + strategy: + fail-fast: false + matrix: + job_id: [1, 2, 3] + steps: + - uses: actions/checkout@v2 + with: + repository: 'apache/dubbo-samples' + ref: master + - name: "Cache local Maven repository" + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: "Restore Dubbo cache" + uses: actions/cache@v2 + with: + path: ~/.m2/repository/org/apache/dubbo + key: ${{ runner.os }}-dubbo-snapshot-${{ github.sha }} + - name: "Download test list" + uses: actions/download-artifact@v2 + with: + name: test-list + path: test/jobs/ + - name: "Set up JDK 8" + uses: actions/setup-java@v1 + with: + java-version: 8 + - name: "Init Candidate Versions" + run: | + DUBBO_VERSION="${{needs.build-source.outputs.version}}" + CANDIDATE_VERSIONS="dubbo.version:$DUBBO_VERSION;$CANDIDATE_VERSIONS" + echo "CANDIDATE_VERSIONS=$CANDIDATE_VERSIONS" >> $GITHUB_ENV + - name: "Build test image" + run: | + cd test && bash ./build-test-image.sh + - name: "Run tests" + run: cd test && bash ./run-tests.sh + - name: "Upload test result" + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-result + path: test/jobs/*-result* + + integration-test-result: + needs: [integration-test-job] + if: always() + runs-on: ubuntu-18.04 + env: + JAVA_VER: 8 + steps: + - uses: actions/checkout@v2 + with: + repository: 'apache/dubbo-samples' + ref: master + - name: "Download test result" + uses: actions/download-artifact@v2 + with: + name: test-result + path: test/jobs/ + - name: "Merge test result" + run: ./test/scripts/merge-test-results.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f13d3e671f9..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: java -sudo: false # faster builds - -jdk: - - openjdk11 - - openjdk8 - -cache: - directories: - - $HOME/.m2 - -install: true - -script: - - rm -rf $HOME/.m2/repository/org/glassfish/javax.el/3.0.1-b08 - - travis_wait 30 ./mvnw --batch-mode --no-transfer-progress clean install -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true - -after_success: - - bash <(curl -s https://codecov.io/bash) - -after_failure: - - echo "build failed!" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c92e4a115b5..a1f163a36b5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,18 @@ -## Contributing to dubbo -Dubbo is released under the non-restrictive Apache 2.0 license, and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. If you want to contribute even something trivial please do not hesitate, but follow the guidelines below. +## Contributing to Dubbo +Dubbo is released under the non-restrictive Apache 2.0 licenses and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. Contributions of all form to this repository is acceptable, as long as it follows the prescribed community guidelines enumerated below. ### Sign the Contributor License Agreement -Before we accept a non-trivial patch or pull request we will need you to sign the Contributor License Agreement. Signing the contributor’s agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team, and given the ability to merge pull requests. +Before we accept a non-trivial patch or pull request (PRs), we will need you to sign the Contributor License Agreement. Signing the contributors' agreement does not grant anyone commits rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors may get invited to join the core team that will grant them privileges to merge existing PRs. ### Contact #### Mailing list -The mailing list is the recommended way for discussing almost anything that related to Dubbo. Please refer to this [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) for detailed documentation on how to subscribe. +The mailing list is the recommended way of pursuing a discussion on almost anything related to Dubbo. Please refer to this [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) for detailed documentation on how to subscribe. -- [dev@dubbo.apache.org](mailto:dev-subscribe@dubbo.apache.org): the develop mailing list, you can ask question here if you have encountered any problem when using or developing Dubbo. -- [commits@dubbo.apache.org](mailto:commits-subscribe@dubbo.apache.org): all the commits will be sent to this mailing list. You can subscribe to it if you are interested in Dubbo's development. +- [dev@dubbo.apache.org](mailto:dev-subscribe@dubbo.apache.org): the developer mailing list where you can ask questions about an issue you may have encountered while working with Dubbo. +- [commits@dubbo.apache.org](mailto:commits-subscribe@dubbo.apache.org): the commit updates will get broadcasted on this mailing list. You can subscribe to it, should you be interested in following Dubbo's development. - [notifications@dubbo.apache.org](mailto:notifications-subscribe@dubbo.apache.org): all the Github [issue](https://github.com/apache/dubbo/issues) updates and [pull request](https://github.com/apache/dubbo/pulls) updates will be sent to this mailing list. ### Reporting issue @@ -21,33 +21,33 @@ Please follow the [template](https://github.com/apache/dubbo/issues/new?templat ### Code Conventions Our code style is almost in line with the standard java conventions (Popular IDE's default setting satisfy this), with the following additional restricts: -* If there are more than 120 characters in current line, start a new line. +* If there are more than 120 characters in the current line, begin a new line. -* Make sure all new .java files to have a simple Javadoc class comment with at least a @date tag identifying birth, and preferably at least a paragraph on what the class is for. +* Make sure all new .java files to have a simple Javadoc class comment with at least a @date tag identifying birth, and preferably at least a paragraph on the intended purpose of the class. * Add the ASF license header comment to all new .java files (copy from existing files in the project) -* Make sure no @author tag added to the file you contribute since @author tag is not used at Apache, other ways such as cvs will record all your contributions fairly. +* Make sure no @author tag gets appended to the file you contribute to as the @author tag is incompatible with Apache. Rest assured, other ways, including CVS, will ensure transparency, fairness in recording your contributions. * Add some Javadocs and, if you change the namespace, some XSD doc elements. -* A few unit tests should be added for a new feature or an important bugfix. +* Sufficient unit-tests should accompany new feature development or non-trivial bug fixes. -* If no-one else is using your branch, please rebase it against the current master (or other target branch in the main project). +* If no-one else is using your branch, please rebase it against the current master (or another target branch in the main project). -* When writing a commit message please follow these conventions, if you are fixing an existing issue please add Fixes #XXX at the end of the commit message (where XXX is the issue number). +* When writing a commit message, please follow the following conventions: should your commit address an open issue, please add Fixes #XXX at the end of the commit message (where XXX is the issue number). ### Contribution flow -This is a rough outline of what a contributor's workflow looks like: +A rough outline of an ideal contributors' workflow is as follows: * Fork the current repository -* Create a topic branch from where to base the contribution. This is usually master. +* Create a topic branch from where to base the contribution. Mostly, it's the master branch. * Make commits of logical units. -* Make sure commit messages are in the proper format (see below). +* Make sure the commit messages are in the proper format (see below). * Push changes in a topic branch to your forked repository. * Follow the checklist in the [pull request template](https://github.com/apache/dubbo/blob/master/PULL_REQUEST_TEMPLATE.md) -* Before you sending out the pull request, please sync your forked repository with remote repository, this will make your pull request simple and clear. See guide below: +* Before sending out the pull request, please sync your forked repository with the remote repository to ensure that your PR is elegant, concise. Reference the guide below: ``` git remote add upstream git@github.com:apache/dubbo.git git fetch upstream @@ -62,15 +62,15 @@ Thanks for contributing! ### Code style -We provide a template file [dubbo_codestyle_for_idea.xml](https://github.com/apache/dubbo/tree/master/codestyle/dubbo_codestyle_for_idea.xml) for IntelliJ idea, you can import it to you IDE. -If you use Eclipse you can config manually by referencing the same file. +We provide a template file [dubbo_codestyle_for_idea.xml](https://github.com/apache/dubbo/tree/master/codestyle/dubbo_codestyle_for_idea.xml) for IntelliJ idea that you can import it to your workplace. +If you use Eclipse, you can use the IntelliJ Idea template for manually configuring your file. **NOTICE** -It is very important to set the dubbo_codestyle_for_idea.xml, otherwise you will fail to pass the Travis CI. Steps to set the code style are as below: +It's critical to set the dubbo_codestyle_for_idea.xml to avoid the failure of your Travis CI builds. Steps to configure the code styles are as follows: 1. Enter `Editor > Code Style` -2. To manage a code style scheme, in the Code Style page, select the desired scheme from the drop-down list, and click ![manage profiles](codestyle/manage_profiles.png). -From the drop-down list, select `Import Scheme`, then select this option `IntelliJ IDEA code style XML` to import scheme +2. To manage a code style scheme, in the Code Style page, select the desired scheme from the drop-down list, and click on ![manage profiles](codestyle/manage_profiles.png). +From the drop-down list, select `Import Scheme`, then choose the option `IntelliJ IDEA code style XML` to import the scheme. 3. In the Scheme field, type the name of the new scheme and press ⏎ to save the changes. diff --git a/Jenkinsfile b/Jenkinsfile index 15eda401fe4..3d9993f0a30 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,12 +12,12 @@ pipeline { } environment { - JAVA_HOME = "${tool 'JDK 1.8 (latest)'}" + JAVA_HOME = "${tool 'jdk_1.8_latest'}" } tools { - maven 'Maven 3 (latest)' - jdk 'JDK 1.8 (latest)' + maven 'maven_3_latest' + jdk 'jdk_1.8_latest' } triggers { @@ -38,7 +38,7 @@ pipeline { stage('Duplicate deploy check') { steps { script { - def deployedCommitId = sh(returnStdout: true, script: "curl --silent https://builds.apache.org/job/Apache%20Dubbo/job/${env.JOB_BASE_NAME}/lastSuccessfulBuild/artifact/DEPLOY_COMMIT_ID || true").trim() + def deployedCommitId = sh(returnStdout: true, script: "curl --silent https://ci-builds.apache.org/job/Dubbo/job/${env.JOB_BASE_NAME}/lastSuccessfulBuild/artifact/DEPLOY_COMMIT_ID || true").trim() env.DEPLOYED_COMMIT_ID = deployedCommitId def commitId = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() env.COMMIT_ID = commitId @@ -65,22 +65,26 @@ pipeline { def commitId = env.COMMIT_ID println "Current commit id: $commitId" - def commitStatusJson = sh(script: "curl --silent https://api.github.com/repos/apache/dubbo/commits/$commitId/status", returnStdout: true).trim() - println "Commit status: \r\n$commitStatusJson" + def commitStatusJson = sh(script: "curl --silent https://api.github.com/repos/apache/dubbo/actions/runs", returnStdout: true).trim() def jsonSlurper = new JsonSlurper() def jsonObject = jsonSlurper.parseText(commitStatusJson) - def status = jsonObject.state - - println "Current commit status is $status" - - if (status == "success") { - env.STATUS_CHECK = "true" - println "Continue to deploy snapshot" - } else { - env.STATUS_CHECK = "false" - println "Current commit status not allow to deploy snapshot" + def runs = jsonObject.workflow_runs + + for (def run in runs) { + if (run.workflow_id == 5030221 && run.head_sha == commitId && + run.event == "push" && run.head_branch == "master") { + println "Find github action for current commit: $run" + if (run.status == "completed" && run.conclusion == "success") { + env.STATUS_CHECK = "true" + println "CI status is success for commitId:$commitId, continue to deploy" + } else { + env.STATUS_CHECK = "false" + println "CI status is not success for commitId:$commitId" + } + break; + } } } } diff --git a/NOTICE b/NOTICE index 9664603fddd..e414ebfbe51 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache Dubbo -Copyright 2018-2020 The Apache Software Foundation +Copyright 2018-2021 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 20c5854a2fd..a1384f243a0 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,18 +1,16 @@ ## What is the purpose of the change -XXXXX + ## Brief changelog -XXXXX ## Verifying this change -XXXXX -Follow this checklist to help us incorporate your contribution quickly and easily: + -- [x] Make sure there is a [GITHUB_issue](https://github.com/apache/dubbo/issues) field for the change (usually before you start working on it). Trivial changes like typos do not require a GITHUB issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. +- [x] Make sure there is a [GitHub_issue](https://github.com/apache/dubbo/issues) field for the change (usually before you start working on it). Trivial changes like typos do not require a GitHub issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. - [ ] Format the pull request title like `[Dubbo-XXX] Fix UnknownException when host config not exist #XXX`. Each commit in the pull request should have a meaningful subject line and body. - [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. - [ ] Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add sample in [dubbo samples](https://github.com/apache/dubbo-samples) project. diff --git a/README.md b/README.md index 153121d2fcf..2c74ca03a2e 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ # Apache Dubbo Project -[![Build Status](https://travis-ci.org/apache/dubbo.svg?branch=master)](https://travis-ci.org/apache/dubbo) -[![codecov](https://codecov.io/gh/apache/dubbo/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo) -![maven](https://img.shields.io/maven-central/v/org.apache.dubbo/dubbo.svg) -![license](https://img.shields.io/github/license/alibaba/dubbo.svg) +[![Build Status](https://github.com/apache/dubbo/workflows/Build%20and%20Test/badge.svg?branch=master)](https://github.com/apache/dubbo/actions/workflows/build-and-test.yml?query=branch%3Amaster+) +[![Codecov](https://codecov.io/gh/apache/dubbo/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo) +![Maven](https://img.shields.io/maven-central/v/org.apache.dubbo/dubbo.svg) +![License](https://img.shields.io/github/license/alibaba/dubbo.svg) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/apache/dubbo.svg)](http://isitmaintained.com/project/apache/dubbo "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/apache/dubbo.svg)](http://isitmaintained.com/project/apache/dubbo "Percentage of issues still open") [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Apache%20Dubbo%20is%20a%20high-performance%2C%20java%20based%2C%20open%20source%20RPC%20framework.&url=http://dubbo.apache.org/&via=ApacheDubbo&hashtags=rpc,java,dubbo,micro-service) -[![](https://img.shields.io/twitter/follow/ApacheDubbo.svg?label=Follow&style=social&logoWidth=0)](https://twitter.com/intent/follow?screen_name=ApacheDubbo) +[![Twitter Follow](https://img.shields.io/twitter/follow/ApacheDubbo.svg?label=Follow&style=social&logoWidth=0)](https://twitter.com/intent/follow?screen_name=ApacheDubbo) [![Gitter](https://badges.gitter.im/alibaba/dubbo.svg)](https://gitter.im/alibaba/dubbo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -Apache Dubbo is a high-performance, Java based open source RPC framework. Please visit [official site](http://dubbo.apache.org) for quick start and documentations, as well as [Wiki](https://github.com/apache/dubbo/wiki) for news, FAQ, and release notes. +Apache Dubbo is a high-performance, Java-based open-source RPC framework. Please visit the [official site](http://dubbo.apache.org) for the quick start guide and documentation, as well as the [wiki](https://github.com/apache/dubbo/wiki) for news, FAQ, and release notes. -We are now collecting dubbo user info in order to help us to improve Dubbo better, pls. kindly help us by providing yours on [issue#1012: Wanted: who's using dubbo](https://github.com/apache/dubbo/issues/1012), thanks :) +We are now collecting Dubbo user info to help us to improve Dubbo further. Kindly support us by providing your usage information on [issue#1012: Wanted: who's using dubbo](https://github.com/apache/dubbo/issues/1012), thanks :) ## Architecture -![Architecture](http://dubbo.apache.org/img/architecture.png) +![Architecture](https://dubbo.apache.org/imgs/architecture.png) ## Features @@ -29,22 +29,22 @@ We are now collecting dubbo user info in order to help us to improve Dubbo bette ## Getting started -The following code snippet comes from [Dubbo Samples](https://github.com/apache/dubbo-samples/tree/master/java/dubbo-samples-api). You may clone the sample project and step into `dubbo-samples-api` sub directory before read on. +The following code snippet comes from [Dubbo Samples](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-api). You may clone the sample project and step into the `dubbo-samples-api` subdirectory before proceeding. ```bash # git clone https://github.com/apache/dubbo-samples.git -# cd dubbo-samples/java/dubbo-samples-api +# cd dubbo-samples/dubbo-samples-api ``` -There's a [README](https://github.com/apache/dubbo-samples/tree/master/java/dubbo-samples-api/README.md) file under `dubbo-samples-api` directory. Read it and try this sample out by following the instructions. +There's a [README](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-api/README.md) file under `dubbo-samples-api` directory. We recommend referencing the samples in that directory by following the below-mentioned instructions: ### Maven dependency ```xml - 2.7.8 + 2.7.10 - + org.apache.dubbo @@ -70,7 +70,7 @@ public interface GreetingsService { } ``` -*See [api/GreetingsService.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/api/GreetingsService.java) on GitHub.* +*See [api/GreetingsService.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/api/GreetingsService.java) on GitHub.* ### Implement service interface for the provider @@ -87,7 +87,7 @@ public class GreetingsServiceImpl implements GreetingsService { } ``` -*See [provider/GreetingsServiceImpl.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java) on GitHub.* +*See [provider/GreetingsServiceImpl.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java) on GitHub.* ### Start service provider @@ -119,7 +119,7 @@ public class Application { } ``` -*See [provider/Application.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/Application.java) on GitHub.* +*See [provider/Application.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/Application.java) on GitHub.* ### Build and run the provider @@ -128,7 +128,7 @@ public class Application { # mvn -Djava.net.preferIPv4Stack=true -Dexec.mainClass=org.apache.dubbo.samples.provider.Application exec:java ``` -### Call remote service in consumer +### Call remote service in the consumer ```java package org.apache.dubbo.samples.client; @@ -153,7 +153,7 @@ public class Application { } } ``` -*See [consumer/Application.java](https://github.com/apache/dubbo-samples/blob/master/java/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/client/Application.java) on GitHub.* +*See [consumer/Application.java](https://github.com/apache/dubbo-samples/blob/master/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/client/Application.java) on GitHub.* ### Build and run the consumer @@ -167,14 +167,14 @@ The consumer will print out `hi, dubbo` on the screen. ### Next steps -* [Your first Dubbo application](http://dubbo.apache.org/en-us/blog/dubbo-101.html) - A 101 tutorial to reveal more details, with the same code above. -* [Dubbo user manual](http://dubbo.apache.org/en-us/docs/user/preface/background.html) - How to use Dubbo and all its features. -* [Dubbo developer guide](http://dubbo.apache.org/en-us/docs/dev/build.html) - How to involve in Dubbo development. -* [Dubbo admin manual](http://dubbo.apache.org/en-us/docs/admin/install/provider-demo.html) - How to admin and manage Dubbo services. +* [Your first Dubbo application](http://dubbo.apache.org/blog/2018/08/07/dubbo-101/) - A 101 tutorial to reveal more details, with the same code above. +* [Dubbo user manual](http://dubbo.apache.org/docs/v2.7/user/preface/background/) - How to use Dubbo and all its features. +* [Dubbo developer guide](http://dubbo.apache.org/docs/v2.7/dev/build/) - How to involve in Dubbo development. +* [Dubbo admin manual](http://dubbo.apache.org/docs/v2.7/admin/install/provider-demo/) - How to admin and manage Dubbo services. ## Building -If you want to try out the cutting-edge features, you can build with the following commands. (Java 1.8 is required to build the master branch) +If you want to try out the cutting-edge features, you can build with the following commands. (Java 1.8 is needed to build the master branch) ``` mvn clean install @@ -182,11 +182,11 @@ If you want to try out the cutting-edge features, you can build with the followi ## Contact -* Mailing list: +* Mailing list: * dev list: for dev/user discussion. [subscribe](mailto:dev-subscribe@dubbo.apache.org), [unsubscribe](mailto:dev-unsubscribe@dubbo.apache.org), [archive](https://lists.apache.org/list.html?dev@dubbo.apache.org), [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) - + * Bugs: [Issues](https://github.com/apache/dubbo/issues/new?template=dubbo-issue-report-template.md) -* Gitter: [Gitter channel](https://gitter.im/alibaba/dubbo) +* Gitter: [Gitter channel](https://gitter.im/alibaba/dubbo) * Twitter: [@ApacheDubbo](https://twitter.com/ApacheDubbo) ## Contributing @@ -195,18 +195,18 @@ See [CONTRIBUTING](https://github.com/apache/dubbo/blob/master/CONTRIBUTING.md) ### How can I contribute? -* Take a look at issues with tag called [`Good first issue`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) or [`Help wanted`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). -* Join the discussion on mailing list, subscription [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide). +* Take a look at issues with tags marked [`Good first issue`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) or [`Help wanted`](https://github.com/apache/dubbo/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22). +* Join the discussion on the mailing list, subscription [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide). * Answer questions on [issues](https://github.com/apache/dubbo/issues). -* Fix bugs reported on [issues](https://github.com/apache/dubbo/issues), and send us pull request. +* Fix bugs reported on [issues](https://github.com/apache/dubbo/issues), and send us a pull request. * Review the existing [pull request](https://github.com/apache/dubbo/pulls). * Improve the [website](https://github.com/apache/dubbo-website), typically we need * blog post * translation on documentation - * use cases about how Dubbo is being used in enterprise system. + * use cases around the integration of Dubbo in enterprise systems. * Improve the [dubbo-admin/dubbo-monitor](https://github.com/apache/dubbo-admin). * Contribute to the projects listed in [ecosystem](https://github.com/dubbo). -* Any form of contribution that is not mentioned above. +* Other forms of contribution not explicitly enumerated above. * If you would like to contribute, please send an email to dev@dubbo.apache.org to let us know! ## Reporting bugs @@ -215,7 +215,7 @@ Please follow the [template](https://github.com/apache/dubbo/issues/new?template ## Reporting a security vulnerability -Please report security vulnerability to [us](mailto:security@dubbo.apache.org) privately. +Please report security vulnerabilities to [us](mailto:security@dubbo.apache.org) privately. ## Dubbo ecosystem @@ -228,12 +228,12 @@ Please report security vulnerability to [us](mailto:security@dubbo.apache.org) p #### Language +* [Go](https://github.com/dubbo/dubbo-go) (recommended) * [Node.js](https://github.com/apache/dubbo-js) * [Python](https://github.com/dubbo/py-client-for-apache-dubbo) * [PHP](https://github.com/apache/dubbo-php-framework) -* [Go](https://github.com/dubbo/dubbo-go) * [Erlang](https://github.com/apache/dubbo-erlang) ## License -Apache Dubbo is under the Apache 2.0 license. See the [LICENSE](https://github.com/apache/dubbo/blob/master/LICENSE) file for details. +Apache Dubbo software is licenced under the Apache License Version 2.0. See the [LICENSE](https://github.com/apache/dubbo/blob/master/LICENSE) file for details. diff --git a/SECURITY.md b/SECURITY.md index ac0c2ad2255..b8b2a2cae8c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported Versions -Below is a table that shows versions that accept security fix. +Below is a table that shows versions that we accept security fixes. | Version | Supported | | ------- | ------------------ | @@ -15,11 +15,11 @@ Below is a table that shows versions that accept security fix. The Apache Software Foundation takes a rigorous standpoint in annihilating the security issues in its software projects. Apache Dubbo is highly sensitive and forthcoming to issues pertaining to its features and functionality. -If you have apprehensions regarding Dubbo's security or you discover vulnerability or potential threat, don’t hesitate to get in touch with the Apache Dubbo Security Team by dropping a mail at security@dubbo.apache.org. In the mail, specify the description of the issue or potential threat. You are also urged to recommend the way to reproduce and replicate the issue. The Dubbo community will get back to you after assessing and analysing the findings. +If you have apprehensions regarding Dubbo's security or you discover vulnerability or potential threat, don’t hesitate to get in touch with the Apache Dubbo Security Team by dropping a mail at security@dubbo.apache.org. In the email, specify the description of the issue or potential threat. You are also urged to recommend the way to reproduce and replicate the issue. The Dubbo community will get back to you after assessing and analysing the findings. PLEASE PAY ATTENTION to report the security issue on the security email before disclosing it on public domain. -## VULNERABILITY HANDLING +## Vulnerability Handling An overview of the vulnerability handling process is: diff --git a/dubbo-all/pom.xml b/dubbo-all/pom.xml index 050ae247471..14a98e8217b 100644 --- a/dubbo-all/pom.xml +++ b/dubbo-all/pom.xml @@ -544,6 +544,13 @@ compile true + + org.apache.dubbo + dubbo-metadata-report-failover + ${project.version} + compile + true + @@ -684,6 +691,7 @@ org.apache.dubbo:dubbo-metadata-report-consul org.apache.dubbo:dubbo-metadata-report-etcd org.apache.dubbo:dubbo-metadata-report-nacos + org.apache.dubbo:dubbo-metadata-report-failover org.apache.dubbo:dubbo-serialization-native-hession @@ -859,6 +867,12 @@ META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory + + + META-INF/dubbo/internal/org.apache.dubbo.metadata.store.failover.FailoverCondition + + diff --git a/dubbo-bom/pom.xml b/dubbo-bom/pom.xml index a5b8b87c988..a41b215ed2a 100644 --- a/dubbo-bom/pom.xml +++ b/dubbo-bom/pom.xml @@ -248,6 +248,11 @@ dubbo-registry-multicast ${project.version} + + org.apache.dubbo + dubbo-registry-multiple + ${project.version} + org.apache.dubbo dubbo-registry-zookeeper @@ -399,6 +404,11 @@ dubbo-metadata-report-nacos ${project.version} + + org.apache.dubbo + dubbo-metadata-report-failover + ${project.version} + @@ -431,6 +441,32 @@ dubbo-metadata-definition-protobuf ${project.version} + + + org.apache.dubbo + dubbo-spring-boot-actuator + ${project.version} + + + org.apache.dubbo + dubbo-spring-boot-autoconfigure + ${project.version} + + + org.apache.dubbo + dubbo-spring-boot-actuator-compatible + ${project.version} + + + org.apache.dubbo + dubbo-spring-boot-autoconfigure-compatible + ${project.version} + + + org.apache.dubbo + dubbo-spring-boot-starter + ${project.version} + diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml index bd6297430bf..12408398d5f 100644 --- a/dubbo-cluster/pom.xml +++ b/dubbo-cluster/pom.xml @@ -50,5 +50,11 @@ zookeeper test + + org.apache.dubbo + dubbo-serialization-hessian2 + ${project.parent.version} + test + \ No newline at end of file diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java index 948827a1725..1fdadf8b21e 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Cluster.java @@ -22,7 +22,6 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.cluster.support.FailoverCluster; /** * Cluster. (SPI, Singleton, ThreadSafe) @@ -33,7 +32,8 @@ */ @SPI(Cluster.DEFAULT) public interface Cluster { - String DEFAULT = FailoverCluster.NAME; + + String DEFAULT = "failover"; /** * Merge the directory invokers to a virtual invoker. @@ -56,4 +56,4 @@ static Cluster getCluster(String name, boolean wrap) { } return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(name, wrap); } -} \ No newline at end of file +} diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java index a89bdf471d7..f2eea8bb5aa 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java @@ -25,7 +25,7 @@ * A ClusterInvoker holds a group of normal invokers, stored in a Directory, mapping to one Registry. * The ClusterInvoker implementation usually provides LB or HA policies, like FailoverClusterInvoker. *

- * In multi-registry subscription scenario, the final ClusterInvoker will referr to several sub ClusterInvokers, with each + * In multi-registry subscription scenario, the final ClusterInvoker will refer to several sub ClusterInvokers, with each * sub ClusterInvoker representing one Registry. Take ZoneAwareClusterInvoker as an example, it is specially customized for * multi-registry use cases: first, pick up one ClusterInvoker, then do LB inside the chose ClusterInvoker. * diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java index 5d482642c88..208b0c24259 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java @@ -53,4 +53,6 @@ public interface Directory extends Node { boolean isDestroyed(); + void discordAddresses(); + } \ No newline at end of file diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java index 166479c273e..108810d770f 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java @@ -47,6 +47,8 @@ */ public abstract class AbstractConfigurator implements Configurator { + private static final String TILDE = "~"; + private final URL configuratorUrl; public AbstractConfigurator(URL url) { @@ -76,7 +78,8 @@ public URL configure(URL url) { String configuratorSide = configuratorUrl.getParameter(SIDE_KEY); if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide) && 0 == configuratorUrl.getPort()) { url = configureIfMatch(NetUtils.getLocalHost(), url); - } else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) && url.getPort() == configuratorUrl.getPort()) { + } else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) && + url.getPort() == configuratorUrl.getPort()) { url = configureIfMatch(url.getHost(), url); } } @@ -138,10 +141,13 @@ private URL configureIfMatch(String host, URL url) { for (Map.Entry entry : configuratorUrl.getParameters().entrySet()) { String key = entry.getKey(); String value = entry.getValue(); - if (key.startsWith("~") || APPLICATION_KEY.equals(key) || SIDE_KEY.equals(key)) { - conditionKeys.add(key); + boolean startWithTilde = startWithTilde(key); + if (startWithTilde || APPLICATION_KEY.equals(key) || SIDE_KEY.equals(key)) { + if (startWithTilde) { + conditionKeys.add(key); + } if (value != null && !ANY_VALUE.equals(value) - && !value.equals(url.getParameter(key.startsWith("~") ? key.substring(1) : key))) { + && !value.equals(url.getParameter(startWithTilde ? key.substring(1) : key))) { return url; } } @@ -153,6 +159,13 @@ private URL configureIfMatch(String host, URL url) { return url; } + private boolean startWithTilde(String key) { + if (StringUtils.isNotEmpty(key) && key.startsWith(TILDE)) { + return true; + } + return false; + } + protected abstract URL doConfigure(URL currentUrl, URL configUrl); } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java index 0fc5fa90d32..28952eb02f8 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java @@ -16,33 +16,33 @@ */ package org.apache.dubbo.rpc.cluster.configurator.parser; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONValidator; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.PojoUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig; -import org.yaml.snakeyaml.TypeDescription; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONValidator; import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; import java.util.ArrayList; import java.util.List; import java.util.Map; -import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY; +import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; /** * Config parser */ public class ConfigParser { - public static List parseConfigurators(String rawConfig) { + public static List parseConfigurators(String rawConfig) throws Exception { // compatible url JsonArray, such as [ "override://xxx", "override://xxx" ] if (isJsonArray(rawConfig)) { return parseJsonArray(rawConfig); @@ -72,14 +72,10 @@ private static List parseJsonArray(String rawConfig) { return urls; } - private static T parseObject(String rawConfig) { - Constructor constructor = new Constructor(ConfiguratorConfig.class); - TypeDescription itemDescription = new TypeDescription(ConfiguratorConfig.class); - itemDescription.addPropertyParameters("items", ConfigItem.class); - constructor.addTypeDescription(itemDescription); - - Yaml yaml = new Yaml(constructor); - return yaml.load(rawConfig); + private static T parseObject(String rawConfig) throws Exception { + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(rawConfig); + return (T) PojoUtils.mapToPojo(map, ConfiguratorConfig.class); } private static List serviceItemToUrls(ConfigItem item, ConfiguratorConfig config) { @@ -100,7 +96,10 @@ private static List serviceItemToUrls(ConfigItem item, ConfiguratorConfig c List apps = item.getApplications(); if (CollectionUtils.isNotEmpty(apps)) { - apps.forEach(app -> urls.add(URL.valueOf(urlBuilder.append("&application=").append(app).toString()))); + apps.forEach(app -> { + StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder); + urls.add(URL.valueOf(tmpUrlBuilder.append("&application=").append(app).toString())); + }); } else { urls.add(URL.valueOf(urlBuilder.toString())); } @@ -123,17 +122,18 @@ private static List appItemToUrls(ConfigItem item, ConfiguratorConfig confi services.add("*"); } for (String s : services) { - urlBuilder.append(appendService(s)); - urlBuilder.append(toParameterString(item)); + StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder); + tmpUrlBuilder.append(appendService(s)); + tmpUrlBuilder.append(toParameterString(item)); - urlBuilder.append("&application=").append(config.getKey()); + tmpUrlBuilder.append("&application=").append(config.getKey()); - parseEnabled(item, config, urlBuilder); + parseEnabled(item, config, tmpUrlBuilder); - urlBuilder.append("&category=").append(APP_DYNAMIC_CONFIGURATORS_CATEGORY); - urlBuilder.append("&configVersion=").append(config.getConfigVersion()); + tmpUrlBuilder.append("&category=").append(APP_DYNAMIC_CONFIGURATORS_CATEGORY); + tmpUrlBuilder.append("&configVersion=").append(config.getConfigVersion()); - urls.add(URL.valueOf(urlBuilder.toString())); + urls.add(URL.valueOf(tmpUrlBuilder.toString())); } } return urls; diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java index 1b1170a32a2..95a540c9300 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java @@ -40,7 +40,6 @@ /** * Abstract implementation of Directory: Invoker list returned from this Directory's list method have been filtered by Routers - * */ public abstract class AbstractDirectory implements Directory { @@ -59,10 +58,14 @@ public abstract class AbstractDirectory implements Directory { protected RouterChain routerChain; public AbstractDirectory(URL url) { - this(url, null); + this(url, null, false); + } + + public AbstractDirectory(URL url, boolean isUrlFromRegistry) { + this(url, null, isUrlFromRegistry); } - public AbstractDirectory(URL url, RouterChain routerChain) { + public AbstractDirectory(URL url, RouterChain routerChain, boolean isUrlFromRegistry) { if (url == null) { throw new IllegalArgumentException("url == null"); } @@ -72,8 +75,13 @@ public AbstractDirectory(URL url, RouterChain routerChain) { this.consumedProtocol = this.queryMap.get(PROTOCOL_KEY) == null ? DUBBO : this.queryMap.get(PROTOCOL_KEY); this.url = url.removeParameter(REFER_KEY).removeParameter(MONITOR_KEY); - this.consumerUrl = this.url.setProtocol(consumedProtocol).setPath(path == null ? queryMap.get(INTERFACE_KEY) : path).addParameters(queryMap) - .removeParameter(MONITOR_KEY); + URL consumerUrlFrom = this.url.setProtocol(consumedProtocol) + .setPath(path == null ? queryMap.get(INTERFACE_KEY) : path); + if (isUrlFromRegistry) { + // reserve parameters if url is already a consumer url + consumerUrlFrom = consumerUrlFrom.clearParameters(); + } + this.consumerUrl = consumerUrlFrom.addParameters(queryMap).removeParameter(MONITOR_KEY); setRouterChain(routerChain); } @@ -123,6 +131,11 @@ public void destroy() { destroyed = true; } + @Override + public void discordAddresses() { + // do nothing by default + } + protected abstract List> doList(Invocation invocation) throws RpcException; } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java index 0595c6366ef..db1b2024838 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java @@ -49,7 +49,7 @@ public StaticDirectory(URL url, List> invokers) { } public StaticDirectory(URL url, List> invokers, RouterChain routerChain) { - super(url == null && CollectionUtils.isNotEmpty(invokers) ? invokers.get(0).getUrl() : url, routerChain); + super(url == null && CollectionUtils.isNotEmpty(invokers) ? invokers.get(0).getUrl() : url, routerChain, false); if (CollectionUtils.isEmpty(invokers)) { throw new IllegalArgumentException("invokers == null"); } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java index adfd04b3161..50215d699bd 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java @@ -49,20 +49,17 @@ protected Invoker doSelect(List> invokers, URL url, Invocation int length = invokers.size(); // Every invoker has the same weight? boolean sameWeight = true; - // the weight of every invokers + // the maxWeight of every invokers, the minWeight = 0 or the maxWeight of the last invoker int[] weights = new int[length]; - // the first invoker's weight - int firstWeight = getWeight(invokers.get(0), invocation); - weights[0] = firstWeight; // The sum of weights - int totalWeight = firstWeight; - for (int i = 1; i < length; i++) { + int totalWeight = 0; + for (int i = 0; i < length; i++) { int weight = getWeight(invokers.get(i), invocation); - // save for later use - weights[i] = weight; // Sum totalWeight += weight; - if (sameWeight && weight != firstWeight) { + // save for later use + weights[i] = totalWeight; + if (sameWeight && totalWeight != weight * (i + 1)) { sameWeight = false; } } @@ -71,8 +68,7 @@ protected Invoker doSelect(List> invokers, URL url, Invocation int offset = ThreadLocalRandom.current().nextInt(totalWeight); // Return a invoker based on the random value. for (int i = 0; i < length; i++) { - offset -= weights[i]; - if (offset < 0) { + if (offset < weights[i]) { return invokers.get(i); } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java index dbf32fde8dc..ce767ff3b5f 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java @@ -50,7 +50,11 @@ /** * ConditionRouter - * + * It supports the conditional routing configured by "override://", in 2.6.x, + * refer to https://dubbo.apache.org/en/docs/v2.7/user/examples/routing-rule/ . + * For 2.7.x and later, please refer to {@link org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouter} + * and {@link org.apache.dubbo.rpc.cluster.router.condition.config.AppRouter} + * refer to https://dubbo.apache.org/zh/docs/v2.7/user/examples/routing-rule/ . */ public class ConditionRouter extends AbstractRouter { public static final String NAME = "condition"; diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java index a616d8d0152..7701c4c62e2 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterFactory.java @@ -22,7 +22,7 @@ /** * ConditionRouterFactory - * + * Load when "override://" is configured {@link ConditionRouter} */ public class ConditionRouterFactory implements RouterFactory { diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java index 07cc7ede2d8..f64ae007cf8 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java @@ -17,9 +17,12 @@ package org.apache.dubbo.rpc.cluster.router.condition.config.model; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.PojoUtils; import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; + +import java.util.Map; /** * %YAML1.2 @@ -37,11 +40,10 @@ */ public class ConditionRuleParser { - public static ConditionRouterRule parse(String rawRule) { - Constructor constructor = new Constructor(ConditionRouterRule.class); - - Yaml yaml = new Yaml(constructor); - ConditionRouterRule rule = yaml.load(rawRule); + public static ConditionRouterRule parse(String rawRule) throws Exception { + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(rawRule); + ConditionRouterRule rule = PojoUtils.mapToPojo(map, ConditionRouterRule.class); rule.setRawRule(rawRule); if (CollectionUtils.isEmpty(rule.getConditions())) { rule.setValid(false); diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java index dab69b39a5a..114d1be3239 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java @@ -32,6 +32,13 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -50,6 +57,7 @@ * ScriptRouter */ public class ScriptRouter extends AbstractRouter { + public static final String NAME = "SCRIPT_ROUTER"; private static final int SCRIPT_ROUTER_DEFAULT_PRIORITY = 0; private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class); @@ -62,6 +70,19 @@ public class ScriptRouter extends AbstractRouter { private CompiledScript function; + private AccessControlContext accessControlContext; + + { + //Just give permission of reflect to access member. + Permissions perms = new Permissions(); + perms.add(new RuntimePermission("accessDeclaredMembers")); + // Cast to Certificate[] required because of ambiguity: + ProtectionDomain domain = new ProtectionDomain( + new CodeSource(null, (Certificate[]) null), perms); + accessControlContext = new AccessControlContext( + new ProtectionDomain[]{domain}); + } + public ScriptRouter(URL url) { this.url = url; this.priority = url.getParameter(PRIORITY_KEY, SCRIPT_ROUTER_DEFAULT_PRIORITY); @@ -75,8 +96,6 @@ public ScriptRouter(URL url) { logger.error("route error, rule has been ignored. rule: " + rule + ", url: " + RpcContext.getContext().getUrl(), e); } - - } /** @@ -107,17 +126,23 @@ private ScriptEngine getEngine(URL url) { @Override public List> route(List> invokers, URL url, Invocation invocation) throws RpcException { - try { - Bindings bindings = createBindings(invokers, invocation); - if (function == null) { - return invokers; - } - return getRoutedInvokers(function.eval(bindings)); - } catch (ScriptException e) { - logger.error("route error, rule has been ignored. rule: " + rule + ", method:" + - invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e); + if (engine == null || function == null) { return invokers; } + Bindings bindings = createBindings(invokers, invocation); + return getRoutedInvokers(AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + return function.eval(bindings); + } catch (ScriptException e) { + logger.error("route error, rule has been ignored. rule: " + rule + ", method:" + + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e); + return invokers; + } + } + }, accessControlContext)); + } /** diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java index 77429b58c2f..daf3fb118a4 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java @@ -34,7 +34,6 @@ import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule; import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser; -import java.net.UnknownHostException; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -161,7 +160,7 @@ public List> route(List> invokers, URL url, Invocation * @return */ private List> filterUsingStaticTag(List> invokers, URL url, Invocation invocation) { - List> result = invokers; + List> result; // Dynamic param String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) : invocation.getAttachment(TAG_KEY); @@ -189,19 +188,11 @@ public boolean isForce() { } private boolean isForceUseTag(Invocation invocation) { - return Boolean.valueOf(invocation.getAttachment(FORCE_USE_TAG, url.getParameter(FORCE_USE_TAG, "false"))); + return Boolean.parseBoolean(invocation.getAttachment(FORCE_USE_TAG, url.getParameter(FORCE_USE_TAG, "false"))); } private List> filterInvoker(List> invokers, Predicate> predicate) { - boolean filter = false; - for (int i = 0; i < invokers.size(); ++i) { - Invoker invoker = invokers.get(i); - if (!predicate.test(invoker)) { - filter = true; - break; - } - } - if (!filter) { + if (invokers.stream().allMatch(predicate)) { return invokers; } @@ -227,8 +218,6 @@ private boolean checkAddressMatch(List addresses, String host, int port) if ((ANYHOST_VALUE + ":" + port).equals(address)) { return true; } - } catch (UnknownHostException e) { - logger.error("The format of ip address is invalid in tag route. Address :" + address, e); } catch (Exception e) { logger.error("The format of ip address is invalid in tag route. Address :" + address, e); } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java index 4f6669f1b25..d25f3ddbc58 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java @@ -17,23 +17,22 @@ package org.apache.dubbo.rpc.cluster.router.tag.model; import org.apache.dubbo.common.utils.CollectionUtils; -import org.yaml.snakeyaml.TypeDescription; +import org.apache.dubbo.common.utils.PojoUtils; + import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; + +import java.util.Map; /** * */ public class TagRuleParser { - public static TagRouterRule parse(String rawRule) { - Constructor constructor = new Constructor(TagRouterRule.class); - TypeDescription tagDescription = new TypeDescription(TagRouterRule.class); - tagDescription.addPropertyParameters("tags", Tag.class); - constructor.addTypeDescription(tagDescription); - - Yaml yaml = new Yaml(constructor); - TagRouterRule rule = yaml.load(rawRule); + public static TagRouterRule parse(String rawRule) throws Exception { + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(rawRule); + TagRouterRule rule = PojoUtils.mapToPojo(map, TagRouterRule.class); rule.setRawRule(rawRule); if (CollectionUtils.isEmpty(rule.getTags())) { rule.setValid(false); diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java index ab0623ea4a7..fcfb13200c3 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.rpc.cluster.support; +import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.Invocation; @@ -30,11 +31,13 @@ /** * BroadcastClusterInvoker - * */ public class BroadcastClusterInvoker extends AbstractClusterInvoker { private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class); + private static final String BROADCAST_FAIL_PERCENT_KEY = "broadcast.fail.percent"; + private static final int MAX_BROADCAST_FAIL_PERCENT = 100; + private static final int MIN_BROADCAST_FAIL_PERCENT = 0; public BroadcastClusterInvoker(Directory directory) { super(directory); @@ -47,21 +50,67 @@ public Result doInvoke(final Invocation invocation, List> invokers, L RpcContext.getContext().setInvokers((List) invokers); RpcException exception = null; Result result = null; + URL url = getUrl(); + // The value range of broadcast.fail.threshold must be 0~100. + // 100 means that an exception will be thrown last, and 0 means that as long as an exception occurs, it will be thrown. + // see https://github.com/apache/dubbo/pull/7174 + int broadcastFailPercent = url.getParameter(BROADCAST_FAIL_PERCENT_KEY, MAX_BROADCAST_FAIL_PERCENT); + + if (broadcastFailPercent < MIN_BROADCAST_FAIL_PERCENT || broadcastFailPercent > MAX_BROADCAST_FAIL_PERCENT) { + logger.info(String.format("The value corresponding to the broadcast.fail.percent parameter must be between 0 and 100. " + + "The current setting is %s, which is reset to 100.", broadcastFailPercent)); + broadcastFailPercent = MAX_BROADCAST_FAIL_PERCENT; + } + + int failThresholdIndex = invokers.size() * broadcastFailPercent / MAX_BROADCAST_FAIL_PERCENT; + int failIndex = 0; for (Invoker invoker : invokers) { try { result = invoker.invoke(invocation); - } catch (RpcException e) { - exception = e; - logger.warn(e.getMessage(), e); + if (null != result && result.hasException()) { + Throwable resultException = result.getException(); + if (null != resultException) { + exception = getRpcException(result.getException()); + logger.warn(exception.getMessage(), exception); + if (failIndex == failThresholdIndex) { + break; + } else { + failIndex++; + } + } + } } catch (Throwable e) { - exception = new RpcException(e.getMessage(), e); - logger.warn(e.getMessage(), e); + exception = getRpcException(e); + logger.warn(exception.getMessage(), exception); + if (failIndex == failThresholdIndex) { + break; + } else { + failIndex++; + } } } + if (exception != null) { + if (failIndex == failThresholdIndex) { + logger.debug( + String.format("The number of BroadcastCluster call failures has reached the threshold %s", failThresholdIndex)); + } else { + logger.debug(String.format("The number of BroadcastCluster call failures has not reached the threshold %s, fail size is %s", + failIndex)); + } throw exception; } + return result; } + private RpcException getRpcException(Throwable throwable) { + RpcException rpcException = null; + if (throwable instanceof RpcException) { + rpcException = (RpcException) throwable; + } else { + rpcException = new RpcException(throwable.getMessage(), throwable); + } + return rpcException; + } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java index 3e5f1d2cbf6..d279674445e 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java @@ -17,9 +17,11 @@ package org.apache.dubbo.rpc.cluster.support; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.remoting.Constants; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY; @@ -40,6 +42,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_KEY; /** * ClusterUtils @@ -88,6 +91,9 @@ public static URL mergeUrl(URL remoteUrl, Map localMap) { if(map.containsKey(VERSION_KEY)){ copyOfLocalMap.remove(VERSION_KEY); } + if (map.containsKey(GENERIC_KEY)) { + copyOfLocalMap.remove(GENERIC_KEY); + } copyOfLocalMap.remove(RELEASE_KEY); copyOfLocalMap.remove(DUBBO_VERSION_KEY); @@ -117,4 +123,21 @@ public static URL mergeUrl(URL remoteUrl, Map localMap) { return remoteUrl.clearParameters().addParameters(map); } + public static URL mergeProviderUrl(URL remoteUrl, Map localMap) { + + //urlprocessor => upc + List providerURLMergeProcessors = ExtensionLoader.getExtensionLoader(ProviderURLMergeProcessor.class) + .getActivateExtension(remoteUrl, "upc"); + + if (providerURLMergeProcessors != null && providerURLMergeProcessors.size() > 0) { + for (ProviderURLMergeProcessor providerURLMergeProcessor : providerURLMergeProcessors) { + if (providerURLMergeProcessor.accept(remoteUrl, localMap)) { + return providerURLMergeProcessor.mergeProviderUrl(remoteUrl, localMap); + } + } + } + + return mergeUrl(remoteUrl, localMap); + } + } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java index 3b815cea39c..056aa363812 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConfigUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; @@ -33,7 +34,6 @@ import java.lang.reflect.Array; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -114,7 +114,7 @@ protected Result doInvoke(Invocation invocation, List> invokers, Load if (resultList.isEmpty()) { return AsyncRpcResult.newDefaultAsyncResult(invocation); } else if (resultList.size() == 1) { - return resultList.iterator().next(); + return AsyncRpcResult.newDefaultAsyncResult(resultList.get(0).getValue(), invocation); } if (returnType == void.class) { @@ -130,9 +130,7 @@ protected Result doInvoke(Invocation invocation, List> invokers, Load throw new RpcException("Can not merge result because missing method [ " + merger + " ] in class [ " + returnType.getName() + " ]"); } - if (!Modifier.isPublic(method.getModifiers())) { - method.setAccessible(true); - } + ReflectUtils.makeAccessible(method); result = resultList.remove(0).getValue(); try { if (method.getReturnType() != void.class diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ProviderURLMergeProcessor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ProviderURLMergeProcessor.java new file mode 100644 index 00000000000..9c91f384fba --- /dev/null +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ProviderURLMergeProcessor.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.cluster.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.SPI; + +import java.util.Map; + +@SPI +public interface ProviderURLMergeProcessor { + URL mergeProviderUrl(URL providerUrl, Map localParametersMap); + + boolean accept(URL providerUrl, Map localParametersMap); +} diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationClusterComparator.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationClusterComparator.java new file mode 100644 index 00000000000..72dfdcfca0a --- /dev/null +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationClusterComparator.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.dubbo.rpc.cluster.support.migration; + +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.rpc.Invoker; + +import java.util.List; + +@SPI +public interface MigrationClusterComparator { + + boolean shouldMigrate(List> interfaceInvokers, List> serviceInvokers); +} \ No newline at end of file diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationClusterInvoker.java new file mode 100644 index 00000000000..df93daf2b29 --- /dev/null +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationClusterInvoker.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.dubbo.rpc.cluster.support.migration; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; + +import java.util.concurrent.atomic.AtomicBoolean; + +public interface MigrationClusterInvoker extends ClusterInvoker { + + boolean isServiceInvoker(); + + MigrationRule getMigrationRule(); + + void setMigrationRule(MigrationRule rule); + + void destroyServiceDiscoveryInvoker(ClusterInvoker invoker); + + void discardServiceDiscoveryInvokerAddress(ClusterInvoker invoker); + + void discardInterfaceInvokerAddress(ClusterInvoker invoker); + + void refreshServiceDiscoveryInvoker(); + + void refreshInterfaceInvoker(); + + void destroyInterfaceInvoker(ClusterInvoker invoker); + + boolean isMigrationMultiRegistry(); + + void migrateToServiceDiscoveryInvoker(boolean forceMigrate); + + void reRefer(URL newSubscribeUrl); + + void fallbackToInterfaceInvoker(); + + AtomicBoolean invokersChanged(); + +} diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationRule.java new file mode 100644 index 00000000000..92e34c42a0e --- /dev/null +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationRule.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.cluster.support.migration; + +import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +import java.util.Optional; + +import static org.apache.dubbo.common.constants.RegistryConstants.INIT; + +public class MigrationRule { + private static final String DUBBO_SERVICEDISCOVERY_MIGRATION_KEY = "dubbo.application.service-discovery.migration"; + public static final String DUBBO_SERVICEDISCOVERY_MIGRATION_GROUP = "MIGRATION"; + public static final String RULE_KEY = ApplicationModel.getName() + ".migration"; + + private static DynamicConfiguration configuration = null; + + static { + Optional optional = ApplicationModel.getEnvironment().getDynamicConfiguration(); + optional.ifPresent(dynamicConfiguration -> configuration = dynamicConfiguration); + } + + private String key; + private MigrationStep step = MigrationStep.FORCE_INTERFACE; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public MigrationStep getStep() { + return step; + } + + public void setStep(MigrationStep step) { + this.step = step; + } + + public static MigrationRule parse(String rawRule) { + if (null == configuration) { + return getMigrationRule(null); + } + + if (StringUtils.isBlank(rawRule) || INIT.equals(rawRule)) { + String step = (String)configuration.getInternalProperty(DUBBO_SERVICEDISCOVERY_MIGRATION_KEY); + return getMigrationRule(step); + + } + + Constructor constructor = new Constructor(MigrationRule.class); + Yaml yaml = new Yaml(constructor); + return yaml.load(rawRule); + } + + public static MigrationRule queryRule() { + if (null == configuration) { + return getMigrationRule(null); + } + + String rawRule = configuration.getConfig(MigrationRule.RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION_GROUP); + return parse(rawRule); + } + + private static MigrationRule getMigrationRule(String step) { + MigrationRule rule = new MigrationRule(); + rule.setStep(Enum.valueOf(MigrationStep.class, StringUtils.isBlank(step) ? MigrationStep.APPLICATION_FIRST.name() : step)); + return rule; + } +} diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationStep.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationStep.java new file mode 100644 index 00000000000..653e6c58cde --- /dev/null +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationStep.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.cluster.support.migration; + +public enum MigrationStep { + FORCE_INTERFACE, + APPLICATION_FIRST, + FORCE_APPLICATION +} \ No newline at end of file diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java index 97b7a0b5441..b0be28b7b09 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareClusterInvoker.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.rpc.cluster.support.registry; +import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; @@ -27,12 +28,20 @@ import org.apache.dubbo.rpc.cluster.Directory; import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationClusterComparator; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationClusterInvoker; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationRule; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationStep; import org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker; +import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.PREFERRED_KEY; +import static org.apache.dubbo.common.constants.RegistryConstants.LOADBALANCE_AMONG_REGISTRIES; +import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_ZONE_FORCE; import static org.apache.dubbo.common.constants.RegistryConstants.ZONE_KEY; @@ -50,6 +59,12 @@ public class ZoneAwareClusterInvoker extends AbstractClusterInvoker { private static final Logger logger = LoggerFactory.getLogger(ZoneAwareClusterInvoker.class); + private static final String PREFER_REGISTRY_KEY = REGISTRY_KEY + "." + PREFERRED_KEY; + + private static final String PREFER_REGISTRY_WITH_ZONE_KEY = REGISTRY_KEY + "." + ZONE_KEY; + + private final LoadBalance loadBalanceAmongRegistries = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(LOADBALANCE_AMONG_REGISTRIES); + public ZoneAwareClusterInvoker(Directory directory) { super(directory); } @@ -61,7 +76,7 @@ public Result doInvoke(Invocation invocation, final List> invokers, L for (Invoker invoker : invokers) { ClusterInvoker clusterInvoker = (ClusterInvoker) invoker; if (clusterInvoker.isAvailable() && clusterInvoker.getRegistryUrl() - .getParameter(PREFERRED_KEY, false)) { + .getParameter(PREFER_REGISTRY_KEY, false)) { return clusterInvoker.invoke(invocation); } } @@ -71,7 +86,7 @@ public Result doInvoke(Invocation invocation, final List> invokers, L if (StringUtils.isNotEmpty(zone)) { for (Invoker invoker : invokers) { ClusterInvoker clusterInvoker = (ClusterInvoker) invoker; - if (clusterInvoker.isAvailable() && zone.equals(clusterInvoker.getRegistryUrl().getParameter(ZONE_KEY))) { + if (clusterInvoker.isAvailable() && zone.equals(clusterInvoker.getRegistryUrl().getParameter(PREFER_REGISTRY_WITH_ZONE_KEY))) { return clusterInvoker.invoke(invocation); } } @@ -85,7 +100,7 @@ public Result doInvoke(Invocation invocation, final List> invokers, L // load balance among all registries, with registry weight count in. - Invoker balancedInvoker = select(loadbalance, invocation, invokers, null); + Invoker balancedInvoker = select(loadBalanceAmongRegistries, invocation, invokers, null); if (balancedInvoker.isAvailable()) { return balancedInvoker.invoke(invocation); } @@ -102,4 +117,137 @@ public Result doInvoke(Invocation invocation, final List> invokers, L return invokers.get(0).invoke(invocation); } + @Override + protected List> list(Invocation invocation) throws RpcException { + List> invokers = super.list(invocation); + + if (null == invokers || invokers.size() < 2) { + return invokers; + } + + List> interfaceInvokers = new ArrayList<>(); + List> serviceInvokers = new ArrayList<>(); + boolean addressChanged = false; + for (Invoker invoker : invokers) { + MigrationClusterInvoker migrationClusterInvoker = (MigrationClusterInvoker) invoker; + if (migrationClusterInvoker.isServiceInvoker()) { + serviceInvokers.add(invoker); + } else { + interfaceInvokers.add(invoker); + } + + if (migrationClusterInvoker.invokersChanged().compareAndSet(true, false)) { + addressChanged = true; + } + } + + if (serviceInvokers.isEmpty() || interfaceInvokers.isEmpty()) { + return invokers; + } + + MigrationRule rule = null; + for (Invoker invoker : serviceInvokers) { + MigrationClusterInvoker migrationClusterInvoker = (MigrationClusterInvoker) invoker; + + if (rule == null) { + rule = migrationClusterInvoker.getMigrationRule(); + continue; + } + + // inconsistency rule + if (!rule.equals(migrationClusterInvoker.getMigrationRule())) { + rule = MigrationRule.queryRule(); + break; + } + } + + MigrationStep step = rule.getStep(); + + switch (step) { + case FORCE_INTERFACE: + clusterRefresh(addressChanged, interfaceInvokers); + clusterDestroy(addressChanged, serviceInvokers, true); + if (logger.isDebugEnabled()) { + logger.debug("step is FORCE_INTERFACE"); + } + return interfaceInvokers; + + case APPLICATION_FIRST: + clusterRefresh(addressChanged, serviceInvokers); + clusterRefresh(addressChanged, interfaceInvokers); + + boolean serviceAvailable = !serviceInvokers.isEmpty(); + if (serviceAvailable) { + if (shouldMigrate(addressChanged, serviceInvokers, interfaceInvokers)) { + if (logger.isDebugEnabled()) { + logger.debug("step is APPLICATION_FIRST shouldMigrate true get serviceInvokers"); + } + return serviceInvokers; + } + } + + if (logger.isDebugEnabled()) { + logger.debug("step is APPLICATION_FIRST " + (serviceInvokers.isEmpty() ? "serviceInvokers is empty" : "shouldMigrate false") + " get interfaceInvokers"); + } + + return interfaceInvokers; + + case FORCE_APPLICATION: + clusterRefresh(addressChanged, serviceInvokers); + clusterDestroy(addressChanged, interfaceInvokers, true); + + if (logger.isDebugEnabled()) { + logger.debug("step is FORCE_APPLICATION"); + } + + return serviceInvokers; + } + + throw new UnsupportedOperationException(rule.getStep().name()); + } + + + private boolean shouldMigrate(boolean addressChanged, List> serviceInvokers, List> interfaceInvokers) { + Set detectors = ExtensionLoader.getExtensionLoader(MigrationClusterComparator.class).getSupportedExtensionInstances(); + if (detectors != null && !detectors.isEmpty()) { + return detectors.stream().allMatch(s -> s.shouldMigrate(interfaceInvokers, serviceInvokers)); + } + + // check application level provider available. + List> availableServiceInvokers = serviceInvokers.stream().filter(s -> s.isAvailable()).collect(Collectors.toList()); + return !availableServiceInvokers.isEmpty(); + } + + private void clusterDestroy(boolean addressChanged, List> invokers, boolean destroySub) { + if (addressChanged) { + invokers.forEach(s -> { + MigrationClusterInvoker invoker = (MigrationClusterInvoker) s; + if (invoker.isServiceInvoker()) { + invoker.discardServiceDiscoveryInvokerAddress(invoker); + if (destroySub) { + invoker.destroyServiceDiscoveryInvoker(invoker); + } + } else { + invoker.discardInterfaceInvokerAddress(invoker); + if (destroySub) { + invoker.destroyInterfaceInvoker(invoker); + } + } + }); + } + } + + private void clusterRefresh(boolean addressChanged, List> invokers) { + if (addressChanged) { + invokers.forEach(s -> { + MigrationClusterInvoker invoker = (MigrationClusterInvoker) s; + if (invoker.isServiceInvoker()) { + invoker.refreshServiceDiscoveryInvoker(); + } else { + invoker.refreshInterfaceInvoker(); + } + }); + } + } + } \ No newline at end of file diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java index 0170026ecd6..29f2bbee743 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java @@ -125,7 +125,7 @@ public String toString() { @Override protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { - // The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter. + // The only purpose is to build an interceptor chain, so the cluster related logic doesn't matter. return null; } } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java index a682297cdc4..51d0a497558 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java @@ -162,7 +162,7 @@ private String getMockExceptionMessage(Throwable t, Throwable mt) { /** * Return MockInvoker * Contract: - * directory.list() will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return. + * directory.list() will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return. * if directory.list() returns more than one mock invoker, only one of them will be used. * * @param invocation @@ -174,7 +174,7 @@ private List> selectMockInvoker(Invocation invocation) { if (invocation instanceof RpcInvocation) { //Note the implicit contract (although the description is added to the interface declaration, but extensibility is a problem. The practice placed in the attachment needs to be improved) ((RpcInvocation) invocation).setAttachment(INVOCATION_NEED_MOCK, Boolean.TRUE.toString()); - //directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return. + //directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return. try { invokers = directory.list(invocation); } catch (RpcException e) { diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory index 2a807f070ae..9416bcc4a49 100644 --- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory +++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory @@ -1,5 +1,4 @@ file=org.apache.dubbo.rpc.cluster.router.file.FileRouterFactory -script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory condition=org.apache.dubbo.rpc.cluster.router.condition.ConditionRouterFactory service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactory app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java index d8d616f7ac4..6dcd953d973 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java @@ -89,7 +89,7 @@ public void parseConfiguratorsServiceGroupVersionTest() throws Exception { } @Test - public void parseConfiguratorsServiceMultiAppsTest() throws IOException { + public void parseConfiguratorsServiceMultiAppsTest() throws Exception { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceMultiApps.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); @@ -112,7 +112,7 @@ public void parseConfiguratorsServiceNoRuleTest() { } @Test - public void parseConfiguratorsAppMultiServicesTest() throws IOException { + public void parseConfiguratorsAppMultiServicesTest() throws Exception { try (InputStream yamlStream = this.getClass().getResourceAsStream("/AppMultiServices.yml")) { String yamlFile = streamToString(yamlStream); List urls = ConfigParser.parseConfigurators(yamlFile); @@ -129,7 +129,7 @@ public void parseConfiguratorsAppMultiServicesTest() throws IOException { @Test - public void parseConfiguratorsAppAnyServicesTest() throws IOException { + public void parseConfiguratorsAppAnyServicesTest() throws Exception { try (InputStream yamlStream = this.getClass().getResourceAsStream("/AppAnyServices.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); @@ -144,7 +144,7 @@ public void parseConfiguratorsAppAnyServicesTest() throws IOException { } @Test - public void parseConfiguratorsAppNoServiceTest() throws IOException { + public void parseConfiguratorsAppNoServiceTest() throws Exception { try (InputStream yamlStream = this.getClass().getResourceAsStream("/AppNoService.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); @@ -159,7 +159,7 @@ public void parseConfiguratorsAppNoServiceTest() throws IOException { } @Test - public void parseConsumerSpecificProvidersTest() throws IOException { + public void parseConsumerSpecificProvidersTest() throws Exception { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ConsumerSpecificProviders.yml")) { List urls = ConfigParser.parseConfigurators(streamToString(yamlStream)); Assertions.assertNotNull(urls); @@ -175,7 +175,7 @@ public void parseConsumerSpecificProvidersTest() throws IOException { } @Test - public void parseURLJsonArrayCompatible() { + public void parseURLJsonArrayCompatible() throws Exception { String configData = "[\"override://0.0.0.0/com.xx.Service?category=configurators&timeout=6666&disabled=true&dynamic=false&enabled=true&group=dubbo&priority=1&version=1.0\" ]"; diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java index 8d77aa4ad11..61e5fa42fe2 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalanceTest.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.rpc.cluster.loadbalance; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.Invoker; import org.junit.jupiter.api.Assertions; @@ -129,15 +130,9 @@ public void testNodeCacheShouldRecycle() { try { //change recycle time to 1 ms recycleTimeField = RoundRobinLoadBalance.class.getDeclaredField("RECYCLE_PERIOD"); - recycleTimeField.setAccessible(true); + ReflectUtils.makeAccessible(recycleTimeField); recycleTimeField.setInt(RoundRobinLoadBalance.class, 10); - } catch (NoSuchFieldException e) { - Assertions.assertTrue(true, "getField failed"); - } catch (SecurityException e) { - Assertions.assertTrue(true, "getField failed"); - } catch (IllegalArgumentException e) { - Assertions.assertTrue(true, "getField failed"); - } catch (IllegalAccessException e) { + } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException | SecurityException e) { Assertions.assertTrue(true, "getField failed"); } } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java index 0981719d475..42155755d1e 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java @@ -76,7 +76,7 @@ private void setData(String path, String data) throws Exception { * */ @Test - public void tagRouterRuleParseTest(){ + public void tagRouterRuleParseTest() throws Exception { String tagRouterRuleConfig = "---\n" + "force: false\n" + "runtime: true\n" + diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationRuleTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationRuleTest.java new file mode 100644 index 00000000000..5bd431d778c --- /dev/null +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/migration/MigrationRuleTest.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.dubbo.rpc.cluster.support.migration; + +import org.junit.jupiter.api.Test; + +public class MigrationRuleTest { + + @Test + public void testParse() { + System.out.println("xxx"); + } + +} \ No newline at end of file diff --git a/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory new file mode 100644 index 00000000000..95a85f3bfdb --- /dev/null +++ b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory @@ -0,0 +1 @@ +script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java b/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java index 223fc978014..6bf44917108 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/BaseServiceMetadata.java @@ -18,11 +18,13 @@ import org.apache.dubbo.common.utils.StringUtils; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_VERSION; + /** * 2019-10-10 */ public class BaseServiceMetadata { - public static final char COLON_SEPERATOR = ':'; + public static final char COLON_SEPARATOR = ':'; protected String serviceKey; protected String serviceInterfaceName; @@ -44,7 +46,7 @@ public static String buildServiceKey(String path, String group, String version) public static String versionFromServiceKey(String serviceKey) { int index = serviceKey.indexOf(":"); if (index == -1) { - return null; + return DEFAULT_VERSION; } return serviceKey.substring(index + 1); } @@ -73,7 +75,7 @@ public static String interfaceFromServiceKey(String serviceKey) { public String getDisplayServiceKey() { StringBuilder serviceNameBuilder = new StringBuilder(); serviceNameBuilder.append(serviceInterfaceName); - serviceNameBuilder.append(COLON_SEPERATOR).append(version); + serviceNameBuilder.append(COLON_SEPARATOR).append(version); return serviceNameBuilder.toString(); } @@ -84,7 +86,7 @@ public String getDisplayServiceKey() { * @return */ public static BaseServiceMetadata revertDisplayServiceKey(String displayKey) { - String[] eles = StringUtils.split(displayKey, COLON_SEPERATOR); + String[] eles = StringUtils.split(displayKey, COLON_SEPARATOR); if (eles == null || eles.length < 1 || eles.length > 2) { return new BaseServiceMetadata(); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java index 90e3415ed13..bb91988befe 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Parameters.java @@ -29,7 +29,7 @@ import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX; -import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; +import static org.apache.dubbo.common.constants.CommonConstants.HIDDEN_KEY_PREFIX; /** * Parameters for backward compatibility for version prior to 2.0.5 @@ -100,13 +100,13 @@ public String getDecodedParameter(String key, String defaultValue) { public String getParameter(String key) { String value = parameters.get(key); if (StringUtils.isEmpty(value)) { - value = parameters.get(HIDE_KEY_PREFIX + key); + value = parameters.get(HIDDEN_KEY_PREFIX + key); } if (StringUtils.isEmpty(value)) { value = parameters.get(DEFAULT_KEY_PREFIX + key); } if (StringUtils.isEmpty(value)) { - value = parameters.get(HIDE_KEY_PREFIX + DEFAULT_KEY_PREFIX + key); + value = parameters.get(HIDDEN_KEY_PREFIX + DEFAULT_KEY_PREFIX + key); } return value; } @@ -174,7 +174,7 @@ public boolean hasParameter(String key) { public String getMethodParameter(String method, String key) { String value = parameters.get(method + "." + key); if (StringUtils.isEmpty(value)) { - value = parameters.get(HIDE_KEY_PREFIX + method + "." + key); + value = parameters.get(HIDDEN_KEY_PREFIX + method + "." + key); } if (StringUtils.isEmpty(value)) { return getParameter(key); @@ -244,6 +244,9 @@ public boolean hasMethodParameter(String method, String key) { @Override public boolean equals(Object o) { + if (this == o) { + return true; + } return parameters.equals(o); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java index 02cd142ea83..834ee889aed 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java @@ -55,6 +55,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.convert.Converter.convertIfPossible; @@ -245,6 +246,11 @@ public static URL valueOf(String url) { int port = 0; String path = null; Map parameters = null; + // ignore the url content following '#' + int poundIndex = url.indexOf('#'); + if (poundIndex != -1) { + url = url.substring(0, poundIndex); + } int i = url.indexOf('?'); // separator between body and parameters if (i >= 0) { String[] parts = url.substring(i + 1).split("&"); @@ -1653,8 +1659,9 @@ public Configuration toConfiguration() { public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + ((host == null) ? 0 : host.hashCode()); - result = prime * result + ((parameters == null) ? 0 : parameters.hashCode()); + result = prime * result + ((parameters == null) ? 0 : parametersHashCode()); result = prime * result + ((password == null) ? 0 : password.hashCode()); result = prime * result + ((path == null) ? 0 : path.hashCode()); result = prime * result + port; @@ -1682,9 +1689,19 @@ public boolean equals(Object obj) { if (other.parameters != null) { return false; } - } else if (!parameters.equals(other.parameters)) { + } else if (!parameters.keySet().equals(other.parameters.keySet())) { return false; + } else { + for (String key : parameters.keySet()) { + if (key.equals(TIMESTAMP_KEY)) { + continue; + } + if (!parameters.get(key).equals(other.parameters.get(key))) { + return false; + } + } } + if (!StringUtils.isEquals(password, other.password)) { return false; } @@ -1703,6 +1720,19 @@ public boolean equals(Object obj) { return true; } + private int parametersHashCode() { + int h = 0; + for (Map.Entry next : parameters.entrySet()) { + if (TIMESTAMP_KEY.equals(next.getKey())) { + continue; + } + + h += next.hashCode(); + } + + return h; + } + public static void putMethodParameter(String method, String key, String value, Map> methodParameters) { Map subParameter = methodParameters.computeIfAbsent(method, k -> new HashMap<>()); subParameter.put(key, value); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java index 37afb78143b..06db68e7718 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX; import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING; import static org.apache.dubbo.common.utils.StringUtils.decodeHexByte; import static org.apache.dubbo.common.utils.Utf8Utils.decodeUtf8; @@ -159,7 +160,7 @@ private static URL parseURLBody(String fullURLStr, String decodedBody, Map parameters = null; - int pathEndIdx = encodedURLStr.indexOf("%3F");// '?' + int pathEndIdx = encodedURLStr.toUpperCase().indexOf("%3F");// '?' if (pathEndIdx >= 0) { parameters = parseEncodedParams(encodedURLStr, pathEndIdx + 3); } else { @@ -226,11 +227,29 @@ private static boolean addParam(String str, boolean isEncoded, int nameStart, in if (isEncoded) { String name = decodeComponent(str, nameStart, valueStart - 3, false, tempBuf); String value = decodeComponent(str, valueStart, valueEnd, false, tempBuf); + if (valueStart == valueEnd) { + value = name; + } else { + value = decodeComponent(str, valueStart, valueEnd, false, tempBuf); + } params.put(name, value); + // compatible with lower versions registering "default." keys + if (name.startsWith(DEFAULT_KEY_PREFIX)) { + params.putIfAbsent(name.substring(DEFAULT_KEY_PREFIX.length()), value); + } } else { - String name = str.substring(nameStart, valueStart -1); + String name = str.substring(nameStart, valueStart - 1); String value = str.substring(valueStart, valueEnd); + if (valueStart == valueEnd) { + value = name; + } else { + value = str.substring(valueStart, valueEnd); + } params.put(name, value); + // compatible with lower versions registering "default." keys + if (name.startsWith(DEFAULT_KEY_PREFIX)) { + params.putIfAbsent(name.substring(DEFAULT_KEY_PREFIX.length()), value); + } } return true; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java index b949e6db04a..ddbed77c83d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java @@ -181,7 +181,12 @@ public static String getVersion(Class cls, String defaultVersion) { return defaultVersion; } - String file = codeSource.getLocation().getFile(); + URL location = codeSource.getLocation(); + if (location == null){ + logger.info("No location for class " + cls.getName() + " when getVersion, use default version " + defaultVersion); + return defaultVersion; + } + String file = location.getFile(); if (!StringUtils.isEmpty(file) && file.endsWith(".jar")) { version = getFromFile(file); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java index d37335eb64d..c4463a20ac6 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.LogHelper; import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.common.utils.SerializeClassChecker; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -318,7 +319,7 @@ private static Method getSetterMethod(Class cls, String property, Class va } } if (method != null) { - method.setAccessible(true); + ReflectUtils.makeAccessible(method); } return method; } @@ -341,14 +342,14 @@ private static Object instantiate(Class cl) throws Exception { constructorArgs[i] = getConstructorArg(paramTypes[i]); } try { - constructor.setAccessible(true); + ReflectUtils.makeAccessible(constructor); return constructor.newInstance(constructorArgs); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { LogHelper.warn(logger, e.getMessage(), e); } } - return cl.newInstance(); + return cl.getDeclaredConstructor().newInstance(); } public static Object getConstructorArg(Class cl) { @@ -464,6 +465,7 @@ public static Class name2Class(ClassLoader loader, String name) throws ClassN if (isReferenceType(name)) { name = name.substring(1, name.length() - 1); } + SerializeClassChecker.getInstance().validateClass(name); return Class.forName(name, false, loader); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java index c4ecea6e0dc..252c3aa719f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java @@ -16,6 +16,11 @@ */ package org.apache.dubbo.common.bytecode; +import org.apache.dubbo.common.utils.ArrayUtils; +import org.apache.dubbo.common.utils.ClassUtils; +import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.common.utils.StringUtils; + import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; @@ -24,12 +29,7 @@ import javassist.CtMethod; import javassist.CtNewConstructor; import javassist.CtNewMethod; -import javassist.LoaderClassPath; import javassist.NotFoundException; -import org.apache.dubbo.common.utils.ArrayUtils; -import org.apache.dubbo.common.utils.ClassUtils; -import org.apache.dubbo.common.utils.ReflectUtils; -import org.apache.dubbo.common.utils.StringUtils; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -91,7 +91,7 @@ public static ClassPool getClassPool(ClassLoader loader) { ClassPool pool = POOL_MAP.get(loader); if (pool == null) { pool = new ClassPool(true); - pool.appendClassPath(new LoaderClassPath(loader)); + pool.appendClassPath(new CustomizedLoaderClassPath(loader)); POOL_MAP.put(loader, pool); } return pool; @@ -340,9 +340,7 @@ public Class toClass(ClassLoader loader, ProtectionDomain pd) { return mCtc.toClass(loader, pd); } catch (RuntimeException e) { throw e; - } catch (NotFoundException e) { - throw new RuntimeException(e.getMessage(), e); - } catch (CannotCompileException e) { + } catch (NotFoundException | CannotCompileException e) { throw new RuntimeException(e.getMessage(), e); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java new file mode 100644 index 00000000000..918f2b1639b --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.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.dubbo.common.bytecode; + +import javassist.ClassPath; + +import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.net.URL; + +/** + * A class search-path representing a class loader. + * + *

It is used for obtaining a class file from the given + * class loader by getResourceAsStream(). + * The LoaderClassPath refers to the class loader through + * WeakReference. If the class loader is garbage collected, + * the other search pathes are examined. + * + *

The given class loader must have both getResourceAsStream() + * and getResource(). + * + * @author Bill Burke + * @author Shigeru Chiba + */ +public class CustomizedLoaderClassPath implements ClassPath { + private WeakReference clref; + + /** + * Creates a search path representing a class loader. + */ + public CustomizedLoaderClassPath(ClassLoader cl) { + clref = new WeakReference(cl); + } + + public String toString() { + Object cl = null; + if (clref != null) { + cl = clref.get(); + } + + return cl == null ? "" : cl.toString(); + } + + /** + * Obtains a class file from the class loader. + * This method calls getResourceAsStream(String) + * on the class loader. + */ + public InputStream openClassfile(String classname) { + String cname = classname.replace('.', '/') + ".class"; + ClassLoader cl = (ClassLoader) clref.get(); + if (cl == null) { + return null; // not found + } else { + InputStream result = cl.getResourceAsStream(cname); + if (result == null && (cl != this.getClass().getClassLoader())) { + return this.getClass().getClassLoader().getResourceAsStream(cname); + } + return result; + } + } + + /** + * Obtains the URL of the specified class file. + * This method calls getResource(String) + * on the class loader. + * + * @return null if the class file could not be found. + */ + public URL find(String classname) { + String cname = classname.replace('.', '/') + ".class"; + ClassLoader cl = (ClassLoader) clref.get(); + if (cl == null) { + return null; // not found + } else { + URL url = cl.getResource(cname); + if (url == null && (cl != this.getClass().getClassLoader())) { + return this.getClass().getClassLoader().getResource(cname); + } + return url; + } + } + + /** + * Closes this class path. + */ + public void close() { + clref = null; + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java index f7a84236384..cd16d381ad1 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Mixin.java @@ -174,7 +174,7 @@ public static Mixin mixin(Class[] ics, Class[] dcs, ClassLoader cl) { ccm.setSuperClass(Mixin.class.getName()); ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }"); Class mixin = ccm.toClass(); - return (Mixin) mixin.newInstance(); + return (Mixin) mixin.getDeclaredConstructor().newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java index bb2b40b7547..9fd81fbd7da 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java @@ -20,7 +20,7 @@ import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.ref.Reference; -import java.lang.ref.WeakReference; +import java.lang.ref.SoftReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -50,6 +50,8 @@ public Object invoke(Object proxy, Method method, Object[] args) { private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0); private static final String PACKAGE_NAME = Proxy.class.getPackage().getName(); private static final Map> PROXY_CACHE_MAP = new WeakHashMap>(); + // cache class, avoid PermGen OOM. + private static final Map> PROXY_CLASS_MAP = new WeakHashMap>(); private static final Object PENDING_GENERATION_MARKER = new Object(); @@ -103,8 +105,11 @@ public static Proxy getProxy(ClassLoader cl, Class... ics) { // get cache by class loader. final Map cache; + // cache class + final Map classCache; synchronized (PROXY_CACHE_MAP) { cache = PROXY_CACHE_MAP.computeIfAbsent(cl, k -> new HashMap<>()); + classCache = PROXY_CLASS_MAP.computeIfAbsent(cl, k -> new HashMap<>()); } Proxy proxy = null; @@ -118,14 +123,38 @@ public static Proxy getProxy(ClassLoader cl, Class... ics) { } } - if (value == PENDING_GENERATION_MARKER) { - try { - cache.wait(); - } catch (InterruptedException e) { + // get Class by key. + Object clazzObj = classCache.get(key); + if (null == clazzObj || clazzObj instanceof Reference) { + Class clazz = null; + if (clazzObj instanceof Reference) { + clazz = (Class) ((Reference) clazzObj).get(); + } + + if (null == clazz) { + if (value == PENDING_GENERATION_MARKER) { + try { + cache.wait(); + } catch (InterruptedException e) { + } + } else { + cache.put(key, PENDING_GENERATION_MARKER); + break; + } + } else { + try { + proxy = (Proxy) clazz.newInstance(); + return proxy; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + if (null == proxy) { + cache.remove(key); + } else { + cache.put(key, new SoftReference(proxy)); + } + } } - } else { - cache.put(key, PENDING_GENERATION_MARKER); - break; } } while (true); @@ -158,9 +187,6 @@ public static Proxy getProxy(ClassLoader cl, Class... ics) { if (worked.contains(desc) || Modifier.isStatic(method.getModifiers())) { continue; } - if (ics[i].isInterface() && Modifier.isStatic(method.getModifiers())) { - continue; - } worked.add(desc); int ix = methods.size(); @@ -190,7 +216,7 @@ public static Proxy getProxy(ClassLoader cl, Class... ics) { ccp.setClassName(pcn); ccp.addField("public static java.lang.reflect.Method[] methods;"); ccp.addField("private " + InvocationHandler.class.getName() + " handler;"); - ccp.addConstructor(Modifier.PUBLIC, new Class[]{InvocationHandler.class}, new Class[0], "handler=$1;"); + ccp.addConstructor(Modifier.PUBLIC, new Class[] {InvocationHandler.class}, new Class[0], "handler=$1;"); ccp.addDefaultConstructor(); Class clazz = ccp.toClass(); clazz.getField("methods").set(null, methods.toArray(new Method[0])); @@ -204,6 +230,10 @@ public static Proxy getProxy(ClassLoader cl, Class... ics) { ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }"); Class pc = ccm.toClass(); proxy = (Proxy) pc.newInstance(); + + synchronized (classCache) { + classCache.put(key, new SoftReference>(pc)); + } } catch (RuntimeException e) { throw e; } catch (Exception e) { @@ -220,7 +250,7 @@ public static Proxy getProxy(ClassLoader cl, Class... ics) { if (proxy == null) { cache.remove(key); } else { - cache.put(key, new WeakReference(proxy)); + cache.put(key, new SoftReference<>(proxy)); } cache.notifyAll(); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java index 345b79e39ca..e8fb687b4b7 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java @@ -154,6 +154,12 @@ private static Wrapper makeWrapper(Class c) { // get all public method. boolean hasMethod = hasMethods(methods); if (hasMethod) { + Map sameNameMethodCount = new HashMap<>((int) (methods.length / 0.75f) + 1); + for (Method m : methods) { + sameNameMethodCount.compute(m.getName(), + (key, oldValue) -> oldValue == null ? 1 : oldValue + 1); + } + c3.append(" try{"); for (Method m : methods) { //ignore Object's method. @@ -166,14 +172,8 @@ private static Wrapper makeWrapper(Class c) { int len = m.getParameterTypes().length; c3.append(" && ").append(" $3.length == ").append(len); - boolean override = false; - for (Method m2 : methods) { - if (m != m2 && m.getName().equals(m2.getName())) { - override = true; - break; - } - } - if (override) { + boolean overload = sameNameMethodCount.get(m.getName()) > 1; + if (overload) { if (len > 0) { for (int l = 0; l < len; l++) { c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"") @@ -263,7 +263,7 @@ private static Wrapper makeWrapper(Class c) { for (Method m : ms.values()) { wc.getField("mts" + ix++).set(null, m.getParameterTypes()); } - return (Wrapper) wc.newInstance(); + return (Wrapper) wc.getDeclaredConstructor().newInstance(); } catch (RuntimeException e) { throw e; } catch (Throwable e) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java index 365cb7f36db..d09afadccef 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/ClassUtils.java @@ -22,6 +22,7 @@ import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; @@ -48,10 +49,8 @@ private ClassUtils() { public static Object newInstance(String name) { try { - return forName(name).newInstance(); - } catch (InstantiationException e) { - throw new IllegalStateException(e.getMessage(), e); - } catch (IllegalAccessException e) { + return forName(name).getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new IllegalStateException(e.getMessage(), e); } } @@ -64,7 +63,7 @@ public static Class forName(String[] packages, String className) { for (String pkg : packages) { try { return classForName(pkg + "." + className); - } catch (ClassNotFoundException e2) { + } catch (ClassNotFoundException ignore) { } } } @@ -123,7 +122,7 @@ public static Class classForName(String className) throws ClassNotFoundExcept if (className.indexOf('.') == -1) { try { return arrayForName("java.lang." + className); - } catch (ClassNotFoundException e2) { + } catch (ClassNotFoundException ignore) { // ignore, let the original exception be thrown } } @@ -431,7 +430,7 @@ public static Map toMap(Map.Entry[] entries) { } return map; } - + /** * get simple class name from qualified class name */ @@ -439,7 +438,6 @@ public static String getSimpleClassName(String qualifiedName) { if (null == qualifiedName) { return null; } - int i = qualifiedName.lastIndexOf('.'); return i < 0 ? qualifiedName : qualifiedName.substring(i + 1); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java index 0dc8f881385..dbb561ebe52 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/CtClassBuilder.java @@ -43,17 +43,17 @@ public class CtClassBuilder { private String superClassName = "java.lang.Object"; - private List imports = new ArrayList<>(); + private final List imports = new ArrayList<>(); - private Map fullNames = new HashMap<>(); + private final Map fullNames = new HashMap<>(); - private List ifaces = new ArrayList<>(); + private final List ifaces = new ArrayList<>(); - private List constructors = new ArrayList<>(); + private final List constructors = new ArrayList<>(); - private List fields = new ArrayList<>(); + private final List fields = new ArrayList<>(); - private List methods = new ArrayList<>(); + private final List methods = new ArrayList<>(); public String getClassName() { return className; @@ -146,7 +146,7 @@ public CtClass build(ClassLoader classLoader) throws NotFoundException, CannotCo CtClass ctClass = pool.makeClass(className, pool.get(superClassName)); // add imported packages - imports.stream().forEach(pool::importPackage); + imports.forEach(pool::importPackage); // add implemented interfaces for (String iface : ifaces) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java index eebf5a00b5c..29c624bca70 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java @@ -38,6 +38,9 @@ public class CompositeConfiguration implements Configuration { */ private List configList = new LinkedList(); + //FIXME, consider change configList to SortedMap to replace this boolean status. + private boolean dynamicIncluded; + public CompositeConfiguration() { this(null, null); } @@ -58,6 +61,15 @@ public CompositeConfiguration(Configuration... configurations) { } } + public void setDynamicIncluded(boolean dynamicIncluded) { + this.dynamicIncluded = dynamicIncluded; + } + + //FIXME, consider change configList to SortedMap to replace this boolean status. + public boolean isDynamicIncluded() { + return dynamicIncluded; + } + public void addConfiguration(Configuration configuration) { if (configList.contains(configuration)) { return; @@ -113,4 +125,4 @@ public Object getProperty(String key, Object defaultValue) { } return value != null ? value : defaultValue; } -} +} \ No newline at end of file diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java index a7c06930fd3..ed79f15d98c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java @@ -66,6 +66,10 @@ public static Configuration getGlobalConfiguration() { return ApplicationModel.getEnvironment().getConfiguration(); } + public static Configuration getDynamicGlobalConfiguration() { + return ApplicationModel.getEnvironment().getDynamicGlobalConfiguration(); + } + // FIXME @SuppressWarnings("deprecation") public static int getServerShutdownTimeout() { @@ -92,6 +96,14 @@ public static int getServerShutdownTimeout() { return timeout; } + public static String getDynamicProperty(String property) { + return getDynamicProperty(property, null); + } + + public static String getDynamicProperty(String property, String defaultValue) { + return StringUtils.trim(getDynamicGlobalConfiguration().getString(property, defaultValue)); + } + public static String getProperty(String property) { return getProperty(property, null); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java index b5f24f745d0..cfc1b0139e6 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java @@ -20,6 +20,8 @@ import org.apache.dubbo.common.context.FrameworkExt; import org.apache.dubbo.common.context.LifecycleAdapter; import org.apache.dubbo.common.extension.DisableInject; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.AbstractConfig; import org.apache.dubbo.config.ConfigCenterConfig; import org.apache.dubbo.config.context.ConfigConfigurationAdapter; @@ -32,6 +34,7 @@ import java.util.Optional; public class Environment extends LifecycleAdapter implements FrameworkExt { + private static final Logger logger = LoggerFactory.getLogger(Environment.class); public static final String NAME = "environment"; private final PropertiesConfiguration propertiesConfiguration; @@ -41,6 +44,8 @@ public class Environment extends LifecycleAdapter implements FrameworkExt { private final InmemoryConfiguration appExternalConfiguration; private CompositeConfiguration globalConfiguration; + private CompositeConfiguration dynamicGlobalConfiguration; + private Map externalConfigurationMap = new HashMap<>(); private Map appExternalConfigurationMap = new HashMap<>(); @@ -146,9 +151,6 @@ public synchronized CompositeConfiguration getPrefixedConfiguration(AbstractConf public Configuration getConfiguration() { if (globalConfiguration == null) { globalConfiguration = new CompositeConfiguration(); - if (dynamicConfiguration != null) { - globalConfiguration.addConfiguration(dynamicConfiguration); - } globalConfiguration.addConfiguration(systemConfiguration); globalConfiguration.addConfiguration(environmentConfiguration); globalConfiguration.addConfiguration(appExternalConfiguration); @@ -158,6 +160,21 @@ public Configuration getConfiguration() { return globalConfiguration; } + public Configuration getDynamicGlobalConfiguration() { + if (dynamicGlobalConfiguration == null) { + if (dynamicConfiguration == null) { + if (logger.isWarnEnabled()) { + logger.warn("dynamicConfiguration is null , return globalConfiguration."); + } + return globalConfiguration; + } + dynamicGlobalConfiguration = new CompositeConfiguration(); + dynamicGlobalConfiguration.addConfiguration(dynamicConfiguration); + dynamicGlobalConfiguration.addConfiguration(getConfiguration()); + } + return dynamicGlobalConfiguration; + } + public boolean isConfigCenterFirst() { return configCenterFirst; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java index 8195558cd85..f7c2ec71baa 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java @@ -74,8 +74,12 @@ public String toString() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ConfigChangedEvent)) return false; + if (this == o) { + return true; + } + if (!(o instanceof ConfigChangedEvent)) { + return false; + } ConfigChangedEvent that = (ConfigChangedEvent) o; return Objects.equals(getKey(), that.getKey()) && Objects.equals(getGroup(), that.getGroup()) && diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java index d85f86b11cf..c0a5d9e4087 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/Constants.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common.config.configcenter; +@Deprecated public interface Constants { String CONFIG_CLUSTER_KEY = "config.cluster"; String CONFIG_NAMESPACE_KEY = "config.namespace"; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java index ad708fb4222..09296b67000 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/TreePathDynamicConfiguration.java @@ -26,7 +26,7 @@ import static java.util.Collections.emptySortedSet; import static java.util.Collections.unmodifiableSortedSet; -import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty; import static org.apache.dubbo.common.utils.PathUtils.buildPath; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java index 8ff73385da9..8b4e4554e99 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java @@ -110,6 +110,8 @@ public interface CommonConstants { String IO_THREADS_KEY = "iothreads"; + String KEEP_ALIVE_KEY = "keep.alive"; + int DEFAULT_QUEUES = 0; int DEFAULT_ALIVE = 60 * 1000; @@ -136,7 +138,7 @@ public interface CommonConstants { String GROUP_CHAR_SEPARATOR = ":"; - String HIDE_KEY_PREFIX = "."; + String HIDDEN_KEY_PREFIX = "."; String DOT_REGEX = "\\."; @@ -201,6 +203,8 @@ public interface CommonConstants { String REMOTE_METADATA_STORAGE_TYPE = "remote"; + String GENERIC_KEY = "generic"; + /** * The composite metadata storage type includes {@link #DEFAULT_METADATA_STORAGE_TYPE "local"} and * {@link #REMOTE_METADATA_STORAGE_TYPE "remote"}. @@ -236,6 +240,11 @@ public interface CommonConstants { String PORT_KEY = "port"; String DUBBO_IP_TO_BIND = "DUBBO_IP_TO_BIND"; + /** + * broadcast cluster. + */ + String BROADCAST_CLUSTER = "broadcast"; + /** * The property name for {@link NetworkInterface#getDisplayName() the name of network interface} that * the Dubbo application prefers @@ -364,4 +373,30 @@ public interface CommonConstants { String SENTINEL_REDIS = "sentinel"; String CLUSTER_REDIS = "cluster"; + + /** Pseudo URL prefix for loading from the class path: "classpath:". */ + String CLASSPATH_URL_PREFIX = "classpath:"; + + String DEFAULT_VERSION = "0.0.0"; + + String CLASS_DESERIALIZE_BLOCK_ALL = "dubbo.security.serialize.blockAllClassExceptAllow"; + + String CLASS_DESERIALIZE_ALLOWED_LIST = "dubbo.security.serialize.allowedClassList"; + + String CLASS_DESERIALIZE_BLOCKED_LIST = "dubbo.security.serialize.blockedClassList"; + + String ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE = "dubbo.security.serialize.generic.native-java-enable"; + + String SERIALIZE_BLOCKED_LIST_FILE_PATH = "security/serialize.blockedlist"; + + + /** + * Interface configuration item + * @since 2.7.10 + */ + String ON_CONNECT_KEY = "onconnect"; + + String ON_DISCONNECT_KEY = "ondisconnect"; + + String TOKEN = "token"; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/ConsulConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/ConsulConstants.java new file mode 100644 index 00000000000..5f4438beb6f --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/ConsulConstants.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.common.constants; + +/** + * Common configuration for configCenter, metadata, and registry modules + */ +public interface ConsulConstants { + + int DEFAULT_PORT = 8500; + + int DEFAULT_WATCH_TIMEOUT = 60 * 1000; + + String WATCH_TIMEOUT = "consul-watch-timeout"; + + int INVALID_PORT = 0; + + +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java index 3feeeb20c51..2e4ffa9bb3a 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java @@ -59,10 +59,9 @@ public interface RegistryConstants { String COMPATIBLE_CONFIG_KEY = "compatible_config"; - String REGISTRY_DUPLICATE_KEY = "duplicate"; - - String ENABLE_REGISTRY_DIRECTORY_AUTO_MIGRATION = "enable-auto-migration"; + String REGISTRY_PUBLISH_INTERFACE_KEY = "publish-interface"; + String DUBBO_PUBLISH_INTERFACE_DEFAULT_KEY = "dubbo.application.publish-interface"; /** * The parameter key of Dubbo Registry type * @@ -114,4 +113,10 @@ public interface RegistryConstants { String ZONE_KEY = "zone"; String REGISTRY_SERVICE_REFERENCE_PATH = "org.apache.dubbo.registry.RegistryService"; + + String INIT = "INIT"; + + String MIGRATION_MULTI_REGISTRY = "MIGRATION_MULTI_REGISTRY"; + + String LOADBALANCE_AMONG_REGISTRIES = "random"; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGenerator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGenerator.java index 2bffa050233..3ca53a3037b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGenerator.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGenerator.java @@ -241,7 +241,7 @@ private String generateExtNameNullCheck(String[] value) { } /** - * generate extName assigment code + * generate extName assignment code */ private String generateExtNameAssignment(String[] value, boolean hasInvocation) { // TODO: refactor it diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java index 922b04ac54b..88c12814e88 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java @@ -48,6 +48,7 @@ import java.util.Map; import java.util.ServiceLoader; import java.util.Set; +import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -259,6 +260,8 @@ public List getActivateExtension(URL url, String key, String group) { */ public List getActivateExtension(URL url, String[] values, String group) { List activateExtensions = new ArrayList<>(); + // solve the bug of using @SPI's wrapper method to report a null pointer exception. + TreeMap activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR); List names = values == null ? new ArrayList<>(0) : asList(values); if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { getExtensionClasses(); @@ -281,10 +284,12 @@ public List getActivateExtension(URL url, String[] values, String group) { && !names.contains(name) && !names.contains(REMOVE_VALUE_PREFIX + name) && isActive(activateValue, url)) { - activateExtensions.add(getExtension(name)); + activateExtensionsMap.put(getExtensionClass(name), getExtension(name)); } } - activateExtensions.sort(ActivateComparator.COMPARATOR); + if(!activateExtensionsMap.isEmpty()){ + activateExtensions.addAll(activateExtensionsMap.values()); + } } List loadedExtensions = new ArrayList<>(); for (int i = 0; i < names.size(); i++) { @@ -599,26 +604,25 @@ public T getAdaptiveExtension() { } private IllegalStateException findException(String name) { - for (Map.Entry entry : exceptions.entrySet()) { - if (entry.getKey().toLowerCase().contains(name.toLowerCase())) { - return entry.getValue(); - } - } StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name); - int i = 1; for (Map.Entry entry : exceptions.entrySet()) { - if (i == 1) { - buf.append(", possible causes: "); + if (entry.getKey().toLowerCase().startsWith(name.toLowerCase())) { + if (i == 1) { + buf.append(", possible causes: "); + } + buf.append("\r\n("); + buf.append(i++); + buf.append(") "); + buf.append(entry.getKey()); + buf.append(":\r\n"); + buf.append(StringUtils.toString(entry.getValue())); } + } - buf.append("\r\n("); - buf.append(i++); - buf.append(") "); - buf.append(entry.getKey()); - buf.append(":\r\n"); - buf.append(StringUtils.toString(entry.getValue())); + if (i == 1) { + buf.append(", no related exception was found, please check whether related SPI module is missing."); } return new IllegalStateException(buf.toString()); } @@ -632,7 +636,7 @@ private T createExtension(String name, boolean wrap) { try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { - EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); + EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); @@ -847,6 +851,7 @@ private void loadResource(Map> extensionClasses, ClassLoader cl try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; + String clazz = null; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) { @@ -859,10 +864,12 @@ private void loadResource(Map> extensionClasses, ClassLoader cl int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); - line = line.substring(i + 1).trim(); + clazz = line.substring(i + 1).trim(); + } else { + clazz = line; } - if (line.length() > 0 && !isExcluded(line, excludedPackages)) { - loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name, overridden); + if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) { + loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java index 670aae6de05..2d5780d8f10 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Wrapper.java @@ -16,9 +16,13 @@ */ package org.apache.dubbo.common.extension; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * The annotated class will only work as a wrapper when the condition matches. */ +@Retention(RetentionPolicy.RUNTIME) public @interface Wrapper { /** diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java index 9ad1aaf5438..6c1eacfb56e 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java @@ -27,12 +27,12 @@ /** * OrderComparator */ -public class ActivateComparator implements Comparator { +public class ActivateComparator implements Comparator { - public static final Comparator COMPARATOR = new ActivateComparator(); + public static final Comparator COMPARATOR = new ActivateComparator(); @Override - public int compare(Object o1, Object o2) { + public int compare(Class o1, Class o2) { if (o1 == null && o2 == null) { return 0; } @@ -46,15 +46,15 @@ public int compare(Object o1, Object o2) { return 0; } - Class inf = findSpi(o1.getClass()); + Class inf = findSpi(o1); - ActivateInfo a1 = parseActivate(o1.getClass()); - ActivateInfo a2 = parseActivate(o2.getClass()); + ActivateInfo a1 = parseActivate(o1); + ActivateInfo a2 = parseActivate(o2); if ((a1.applicableToCompare() || a2.applicableToCompare()) && inf != null) { ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(inf); if (a1.applicableToCompare()) { - String n2 = extensionLoader.getExtensionName(o2.getClass()); + String n2 = extensionLoader.getExtensionName(o2); if (a1.isLess(n2)) { return -1; } @@ -65,7 +65,7 @@ public int compare(Object o1, Object o2) { } if (a2.applicableToCompare()) { - String n1 = extensionLoader.getExtensionName(o1.getClass()); + String n1 = extensionLoader.getExtensionName(o1); if (a2.isLess(n1)) { return 1; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java index 3aa3fd9ed1c..63762aebad8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/J2oVisitor.java @@ -17,6 +17,7 @@ package org.apache.dubbo.common.json; import org.apache.dubbo.common.bytecode.Wrapper; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.Stack; import org.apache.dubbo.common.utils.StringUtils; @@ -259,9 +260,7 @@ public void objectBegin() throws ParseException { try { mValue = mType.newInstance(); mWrapper = Wrapper.getWrapper(mType); - } catch (IllegalAccessException e) { - throw new ParseException(StringUtils.toString(e)); - } catch (InstantiationException e) { + } catch (IllegalAccessException | InstantiationException e) { throw new ParseException(StringUtils.toString(e)); } } @@ -300,13 +299,9 @@ public void objectItemValue(Object obj, boolean isValue) throws ParseException { if (mValue instanceof Throwable && "message".equals(name)) { try { Field field = Throwable.class.getDeclaredField("detailMessage"); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtils.makeAccessible(field); field.set(mValue, obj); - } catch (NoSuchFieldException e) { - throw new ParseException(StringUtils.toString(e)); - } catch (IllegalAccessException e) { + } catch (NoSuchFieldException | IllegalAccessException e) { throw new ParseException(StringUtils.toString(e)); } } else if (!CLASS_PROPERTY.equals(name)) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java b/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java index 7c11dd32454..48df09a6dd9 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/json/JSON.java @@ -314,7 +314,7 @@ private static Object parse(Reader reader, int expect) throws IOException, Parse break; } default: - throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } @@ -362,7 +362,7 @@ private static Object parse(Reader reader, int expect) throws IOException, Parse break; } default: - throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } @@ -401,7 +401,7 @@ private static Object parse(Reader reader, int expect) throws IOException, Parse break; } default: - throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } @@ -439,12 +439,12 @@ private static Object parse(Reader reader, int expect) throws IOException, Parse break; } default: - throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } default: - throw new ParseException("Unexcepted state."); + throw new ParseException("Unexpected state."); } } while ((token = jr.nextToken()) != null); @@ -509,7 +509,7 @@ private static Object parse(Reader reader, JSONVisitor handler, int expect) thro break; } default: - throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } @@ -585,7 +585,7 @@ private static Object parse(Reader reader, JSONVisitor handler, int expect) thro break; } default: - throw new ParseException("Unexcepted token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ VALUE or ',' or ']' or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } @@ -636,7 +636,7 @@ private static Object parse(Reader reader, JSONVisitor handler, int expect) thro break; } default: - throw new ParseException("Unexcepted token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ IDENT or VALUE or ',' or '}' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } @@ -686,12 +686,12 @@ private static Object parse(Reader reader, JSONVisitor handler, int expect) thro break; } default: - throw new ParseException("Unexcepted token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); + throw new ParseException("Unexpected token expect [ VALUE or '[' or '{' ] get '" + JSONToken.token2string(token.type) + "'"); } break; } default: - throw new ParseException("Unexcepted state."); + throw new ParseException("Unexpected state."); } } while ((token = jr.nextToken()) != null); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java index babb3ed3c9f..76a6a0b8afa 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/Prioritized.java @@ -63,7 +63,7 @@ public interface Prioritized extends Comparable { /** * Get the priority * - * @return the default is {@link #MIN_PRIORITY minimum one} + * @return the default is {@link #NORMAL_PRIORITY} */ default int getPriority() { return NORMAL_PRIORITY; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java new file mode 100644 index 00000000000..6cc8db8fee4 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalRunnable.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.common.threadlocal; + + +/** + * InternalRunnable + * There is a risk of memory leak when using {@link InternalThreadLocal} without calling + * {@link InternalThreadLocal#removeAll()}. + * This design is learning from {@see io.netty.util.concurrent.FastThreadLocalRunnable} which is in Netty. + */ +public class InternalRunnable implements Runnable{ + private final Runnable runnable; + + public InternalRunnable(Runnable runnable){ + this.runnable=runnable; + } + + /** + * After the task execution is completed, it will call {@link InternalThreadLocal#removeAll()} to clear + * unnecessary variables in the thread. + */ + @Override + public void run() { + try{ + runnable.run(); + }finally { + InternalThreadLocal.removeAll(); + } + } + + /** + * Wrap ordinary Runnable into {@link InternalThreadLocal}. + */ + static Runnable Wrap(Runnable runnable){ + return runnable instanceof InternalRunnable?runnable:new InternalRunnable(runnable); + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java index 8820f12cc2f..13c7463db8f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java @@ -58,7 +58,7 @@ public static void removeAll() { if (v != null && v != InternalThreadLocalMap.UNSET) { Set> variablesToRemove = (Set>) v; InternalThreadLocal[] variablesToRemoveArray = - variablesToRemove.toArray(new InternalThreadLocal[variablesToRemove.size()]); + variablesToRemove.toArray(new InternalThreadLocal[0]); for (InternalThreadLocal tlv : variablesToRemoveArray) { tlv.remove(threadLocalMap); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java index 52b8d562c08..0bb305f03f3 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/NamedInternalThreadFactory.java @@ -40,7 +40,7 @@ public NamedInternalThreadFactory(String prefix, boolean daemon) { @Override public Thread newThread(Runnable runnable) { String name = mPrefix + mThreadNum.getAndIncrement(); - InternalThread ret = new InternalThread(mGroup, runnable, name, 0); + InternalThread ret = new InternalThread(mGroup, InternalRunnable.Wrap(runnable), name, 0); ret.setDaemon(mDaemon); return ret; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java index 0225132e729..bc9042c8e43 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java @@ -32,7 +32,7 @@ /** * The most important difference between this Executor and other normal Executor is that this one doesn't manage * any thread. - * + *

* Tasks submitted to this executor through {@link #execute(Runnable)} will not get scheduled to a specific thread, though normal executors always do the schedule. * Those tasks are stored in a blocking queue and will only be executed when a thread calls {@link #waitAndDrain()}, the thread executing the task * is exactly the same as the one calling waitAndDrain. @@ -86,7 +86,13 @@ public void waitAndDrain() throws InterruptedException { return; } - Runnable runnable = queue.take(); + Runnable runnable; + try { + runnable = queue.take(); + }catch (InterruptedException e){ + waiting = false; + throw e; + } synchronized (lock) { waiting = false; @@ -95,12 +101,7 @@ public void waitAndDrain() throws InterruptedException { runnable = queue.poll(); while (runnable != null) { - try { - runnable.run(); - } catch (Throwable t) { - logger.info(t); - - } + runnable.run(); runnable = queue.poll(); } // mark the status of ThreadlessExecutor as finished. @@ -131,6 +132,7 @@ public long waitAndDrain(long timeout, TimeUnit unit) throws InterruptedExceptio */ @Override public void execute(Runnable runnable) { + runnable = new RunnableWrapper(runnable); synchronized (lock) { if (!waiting) { sharedExecutor.execute(runnable); @@ -180,4 +182,21 @@ public boolean isTerminated() { public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return false; } + + private static class RunnableWrapper implements Runnable { + private Runnable runnable; + + public RunnableWrapper(Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void run() { + try { + runnable.run(); + } catch (Throwable t) { + logger.info(t); + } + } + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java index 1669089ebec..4325ffa9e34 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java @@ -21,6 +21,7 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.ThreadPool; +import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NamedThreadFactory; import java.util.Map; @@ -71,12 +72,9 @@ public DefaultExecutorRepository() { * @return */ public synchronized ExecutorService createExecutorIfAbsent(URL url) { - String componentKey = EXECUTOR_SERVICE_COMPONENT_KEY; - if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { - componentKey = CONSUMER_SIDE; - } - Map executors = data.computeIfAbsent(componentKey, k -> new ConcurrentHashMap<>()); - Integer portKey = url.getPort(); + Map executors = data.computeIfAbsent(EXECUTOR_SERVICE_COMPONENT_KEY, k -> new ConcurrentHashMap<>()); + //issue-7054:Consumer's executor is sharing globally, key=Integer.MAX_VALUE. Provider's executor is sharing by protocol. + Integer portKey = CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY)) ? Integer.MAX_VALUE : url.getPort(); ExecutorService executor = executors.computeIfAbsent(portKey, k -> createExecutor(url)); // If executor has been shut down, create a new one if (executor.isShutdown() || executor.isTerminated()) { @@ -88,12 +86,7 @@ public synchronized ExecutorService createExecutorIfAbsent(URL url) { } public ExecutorService getExecutor(URL url) { - String componentKey = EXECUTOR_SERVICE_COMPONENT_KEY; - if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { - componentKey = CONSUMER_SIDE; - } - Map executors = data.get(componentKey); - + Map executors = data.get(EXECUTOR_SERVICE_COMPONENT_KEY); /** * It's guaranteed that this method is called after {@link #createExecutorIfAbsent(URL)}, so data should already * have Executor instances generated and stored. @@ -103,17 +96,20 @@ public ExecutorService getExecutor(URL url) { "before coming to here."); return null; } - - Integer portKey = url.getPort(); + //issue-7054:Consumer's executor is sharing globally, key=Integer.MAX_VALUE. Provider's executor is sharing by protocol. + Integer portKey = CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY)) ? Integer.MAX_VALUE : url.getPort(); ExecutorService executor = executors.get(portKey); - if (executor != null) { - if (executor.isShutdown() || executor.isTerminated()) { - executors.remove(portKey); - executor = createExecutor(url); - executors.put(portKey, executor); - } + if (executor != null && (executor.isShutdown() || executor.isTerminated())) { + executors.remove(portKey); + // Does not re-create a shutdown executor, use SHARED_EXECUTOR for downgrade. + executor = null; + logger.info("Executor for " + url + " is shutdown."); + } + if (executor == null) { + return SHARED_EXECUTOR; + } else { + return executor; } - return executor; } @Override @@ -159,6 +155,19 @@ public ExecutorService getSharedExecutor() { return SHARED_EXECUTOR; } + @Override + public void destroyAll() { + data.values().forEach(executors -> { + if (executors != null) { + executors.values().forEach(executor -> { + if (executor != null && !executor.isShutdown()) { + ExecutorUtil.shutdownNow(executor, 100); + } + }); + } + }); + } + private ExecutorService createExecutor(URL url) { return (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java index af3b1105aec..b19fcdffcad 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java @@ -64,4 +64,8 @@ public interface ExecutorRepository { */ ExecutorService getSharedExecutor(); + /** + * Destroy all executors that are not in shutdown state + */ + void destroyAll(); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java index c6865eb225c..6347e148315 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java @@ -31,8 +31,10 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent; import org.apache.dubbo.common.utils.JVMUtil; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.event.EventDispatcher; +import static java.lang.String.format; import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY; /** @@ -61,6 +63,8 @@ public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { private static Semaphore guard = new Semaphore(1); + private static final String USER_HOME = System.getProperty("user.home"); + public AbortPolicyWithReport(String threadName, URL url) { this.threadName = threadName; this.url = url; @@ -104,7 +108,7 @@ private void dumpJStack() { ExecutorService pool = Executors.newSingleThreadExecutor(); pool.execute(() -> { - String dumpPath = url.getParameter(DUMP_DIRECTORY, System.getProperty("user.home")); + String dumpPath = getDumpPath(); SimpleDateFormat sdf; @@ -134,4 +138,21 @@ private void dumpJStack() { } + private String getDumpPath() { + final String dumpPath = url.getParameter(DUMP_DIRECTORY); + if (StringUtils.isEmpty(dumpPath)) { + return USER_HOME; + } + final File dumpDirectory = new File(dumpPath); + if (!dumpDirectory.exists()) { + if (dumpDirectory.mkdirs()) { + logger.info(format("Dubbo dump directory[%s] created", dumpDirectory.getAbsolutePath())); + } else { + logger.warn(format("Dubbo dump directory[%s] can't be created, use the 'user.home'[%s]", + dumpDirectory.getAbsolutePath(), USER_HOME)); + return USER_HOME; + } + } + return dumpPath; + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java index d695b46d169..487cc74ba5c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java @@ -805,8 +805,10 @@ private HashedWheelTimeout pollTimeout() { return head; } } - + + private static final boolean IS_OS_WINDOWS = System.getProperty("os.name", "").toLowerCase(Locale.US).contains("win"); + private boolean isWindows() { - return System.getProperty("os.name", "").toLowerCase(Locale.US).contains("win"); + return IS_OS_WINDOWS; } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java index 600c8aaef94..8da5f1d05d9 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java @@ -22,6 +22,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -40,6 +41,7 @@ import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes; import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; import static org.apache.dubbo.common.utils.CollectionUtils.first; +import static org.apache.dubbo.common.utils.MethodUtils.findMethod; import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod; /** @@ -449,4 +451,32 @@ static boolean isAnyAnnotationPresent(Class type, Class... annotationTypes) { return isAnnotationPresent(type, false, annotationTypes); } + + + /** + * Get the default value of attribute on the specified annotation + * + * @param annotation {@link Annotation} object + * @param attributeName the name of attribute + * @param the type of value + * @return null if not found + * @since 2.7.9 + */ + static T getDefaultValue(Annotation annotation, String attributeName) { + return getDefaultValue(annotation.annotationType(), attributeName); + } + + /** + * Get the default value of attribute on the specified annotation + * + * @param annotationType the type of {@link Annotation} + * @param attributeName the name of attribute + * @param the type of value + * @return null if not found + * @since 2.7.9 + */ + static T getDefaultValue(Class annotationType, String attributeName) { + Method method = findMethod(annotationType, attributeName); + return (T) (method == null ? null : method.getDefaultValue()); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java index 5be000d78f4..33af1bc1724 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ArrayUtils.java @@ -50,9 +50,7 @@ public static boolean contains(final String[] array, String valueToFind) { } public static int indexOf(String[] array, String valueToFind, int startIndex) { - if (isEmpty(array) || valueToFind == null) { - return -1; - } else { + if (!isEmpty(array) && valueToFind != null) { if (startIndex < 0) { startIndex = 0; } @@ -63,7 +61,19 @@ public static int indexOf(String[] array, String valueToFind, int startIndex) { } } - return -1; } + return -1; + } + + /** + * Convert from variable arguments to array + * + * @param values variable arguments + * @param The class + * @return array + * @since 2.7.9 + */ + public static T[] of(T... values) { + return values; } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java index 2fb515c9fc3..a437a7d6a7f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ClassUtils.java @@ -43,21 +43,6 @@ public class ClassUtils { * Suffix for array class names: "[]" */ public static final String ARRAY_SUFFIX = "[]"; - /** - * Prefix for internal array class names: "[L" - */ - private static final String INTERNAL_ARRAY_PREFIX = "[L"; - /** - * Map with primitive type name as key and corresponding primitive type as - * value, for example: "int" -> "int.class". - */ - private static final Map> PRIMITIVE_TYPE_NAME_MAP = new HashMap>(32); - /** - * Map with primitive wrapper type as key and corresponding primitive type - * as value, for example: Integer.class -> int.class. - */ - private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap, Class>(16); - /** * Simple Types including: *

    @@ -94,7 +79,20 @@ public class ClassUtils { Date.class, Object.class ); - + /** + * Prefix for internal array class names: "[L" + */ + private static final String INTERNAL_ARRAY_PREFIX = "[L"; + /** + * Map with primitive type name as key and corresponding primitive type as + * value, for example: "int" -> "int.class". + */ + private static final Map> PRIMITIVE_TYPE_NAME_MAP = new HashMap>(32); + /** + * Map with primitive wrapper type as key and corresponding primitive type + * as value, for example: Integer.class -> int.class. + */ + private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap, Class>(16); private static final char PACKAGE_SEPARATOR_CHAR = '.'; static { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java index 1b73370c0ba..13c55eb133b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java @@ -39,28 +39,25 @@ */ public class CollectionUtils { - private static final Comparator SIMPLE_NAME_COMPARATOR = new Comparator() { - @Override - public int compare(String s1, String s2) { - if (s1 == null && s2 == null) { - return 0; - } - if (s1 == null) { - return -1; - } - if (s2 == null) { - return 1; - } - int i1 = s1.lastIndexOf('.'); - if (i1 >= 0) { - s1 = s1.substring(i1 + 1); - } - int i2 = s2.lastIndexOf('.'); - if (i2 >= 0) { - s2 = s2.substring(i2 + 1); - } - return s1.compareToIgnoreCase(s2); + private static final Comparator SIMPLE_NAME_COMPARATOR = (s1, s2) -> { + if (s1 == null && s2 == null) { + return 0; } + if (s1 == null) { + return -1; + } + if (s2 == null) { + return 1; + } + int i1 = s1.lastIndexOf('.'); + if (i1 >= 0) { + s1 = s1.substring(i1 + 1); + } + int i2 = s2.lastIndexOf('.'); + if (i2 >= 0) { + s2 = s2.substring(i2 + 1); + } + return s1.compareToIgnoreCase(s2); }; private CollectionUtils() { @@ -324,9 +321,7 @@ public static boolean equals(Collection one, Collection another) { try { return one.containsAll(another); - } catch (ClassCastException unused) { - return false; - } catch (NullPointerException unused) { + } catch (ClassCastException | NullPointerException unused) { return false; } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java index 5d5cc5c80a5..c73a976cabe 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java @@ -1,223 +1,229 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.common.utils; - -import java.lang.reflect.Array; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class CompatibleTypeUtils { - - private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; - - private CompatibleTypeUtils() { - } - - /** - * Compatible type convert. Null value is allowed to pass in. If no conversion is needed, then the original value - * will be returned. - *

    - * Supported compatible type conversions include (primary types and corresponding wrappers are not listed): - *

      - *
    • String -> char, enum, Date - *
    • byte, short, int, long -> byte, short, int, long - *
    • float, double -> float, double - *
    - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public static Object compatibleTypeConvert(Object value, Class type) { - if (value == null || type == null || type.isAssignableFrom(value.getClass())) { - return value; - } - - if (value instanceof String) { - String string = (String) value; - if (char.class.equals(type) || Character.class.equals(type)) { - if (string.length() != 1) { - throw new IllegalArgumentException(String.format("CAN NOT convert String(%s) to char!" + - " when convert String to char, the String MUST only 1 char.", string)); - } - return string.charAt(0); - } - if (type.isEnum()) { - return Enum.valueOf((Class) type, string); - } - if (type == BigInteger.class) { - return new BigInteger(string); - } - if (type == BigDecimal.class) { - return new BigDecimal(string); - } - if (type == Short.class || type == short.class) { - return new Short(string); - } - if (type == Integer.class || type == int.class) { - return new Integer(string); - } - if (type == Long.class || type == long.class) { - return new Long(string); - } - if (type == Double.class || type == double.class) { - return new Double(string); - } - if (type == Float.class || type == float.class) { - return new Float(string); - } - if (type == Byte.class || type == byte.class) { - return new Byte(string); - } - if (type == Boolean.class || type == boolean.class) { - return Boolean.valueOf(string); - } - if (type == Date.class || type == java.sql.Date.class || type == java.sql.Timestamp.class - || type == java.sql.Time.class) { - try { - Date date = new SimpleDateFormat(DATE_FORMAT).parse(string); - if (type == java.sql.Date.class) { - return new java.sql.Date(date.getTime()); - } - if (type == java.sql.Timestamp.class) { - return new java.sql.Timestamp(date.getTime()); - } - if (type == java.sql.Time.class) { - return new java.sql.Time(date.getTime()); - } - return date; - } catch (ParseException e) { - throw new IllegalStateException("Failed to parse date " + value + " by format " - + DATE_FORMAT + ", cause: " + e.getMessage(), e); - } - } - if (type == java.time.LocalDateTime.class || type == java.time.LocalDate.class - || type == java.time.LocalTime.class) { - - LocalDateTime localDateTime = LocalDateTime.parse(string); - if (type == java.time.LocalDate.class) { - return localDateTime.toLocalDate(); - } - if (type == java.time.LocalTime.class) { - return localDateTime.toLocalTime(); - } - return localDateTime; - } - if (type == Class.class) { - try { - return ReflectUtils.name2class(string); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - if (char[].class.equals(type)) { - // Process string to char array for generic invoke - // See - // - https://github.com/apache/dubbo/issues/2003 - int len = string.length(); - char[] chars = new char[len]; - string.getChars(0, len, chars, 0); - return chars; - } - } - if (value instanceof Number) { - Number number = (Number) value; - if (type == byte.class || type == Byte.class) { - return number.byteValue(); - } - if (type == short.class || type == Short.class) { - return number.shortValue(); - } - if (type == int.class || type == Integer.class) { - return number.intValue(); - } - if (type == long.class || type == Long.class) { - return number.longValue(); - } - if (type == float.class || type == Float.class) { - return number.floatValue(); - } - if (type == double.class || type == Double.class) { - return number.doubleValue(); - } - if (type == BigInteger.class) { - return BigInteger.valueOf(number.longValue()); - } - if (type == BigDecimal.class) { - return BigDecimal.valueOf(number.doubleValue()); - } - if (type == Date.class) { - return new Date(number.longValue()); - } - if (type == boolean.class || type == Boolean.class) { - return 0 != number.intValue(); - } - } - if (value instanceof Collection) { - Collection collection = (Collection) value; - if (type.isArray()) { - int length = collection.size(); - Object array = Array.newInstance(type.getComponentType(), length); - int i = 0; - for (Object item : collection) { - Array.set(array, i++, item); - } - return array; - } - if (!type.isInterface()) { - try { - Collection result = (Collection) type.newInstance(); - result.addAll(collection); - return result; - } catch (Throwable ignored) { - } - } - if (type == List.class) { - return new ArrayList(collection); - } - if (type == Set.class) { - return new HashSet(collection); - } - } - if (value.getClass().isArray() && Collection.class.isAssignableFrom(type)) { - Collection collection; - if (!type.isInterface()) { - try { - collection = (Collection) type.newInstance(); - } catch (Throwable e) { - collection = new ArrayList(); - } - } else if (type == Set.class) { - collection = new HashSet(); - } else { - collection = new ArrayList(); - } - int length = Array.getLength(value); - for (int i = 0; i < length; i++) { - collection.add(Array.get(value, i)); - } - return collection; - } - return value; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.utils; + +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class CompatibleTypeUtils { + + private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + private CompatibleTypeUtils() { + } + + /** + * Compatible type convert. Null value is allowed to pass in. If no conversion is needed, then the original value + * will be returned. + *

    + * Supported compatible type conversions include (primary types and corresponding wrappers are not listed): + *

      + *
    • String -> char, enum, Date + *
    • byte, short, int, long -> byte, short, int, long + *
    • float, double -> float, double + *
    + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Object compatibleTypeConvert(Object value, Class type) { + if (value == null || type == null || type.isAssignableFrom(value.getClass())) { + return value; + } + + if (value instanceof String) { + String string = (String) value; + if (char.class.equals(type) || Character.class.equals(type)) { + if (string.length() != 1) { + throw new IllegalArgumentException(String.format("CAN NOT convert String(%s) to char!" + + " when convert String to char, the String MUST only 1 char.", string)); + } + return string.charAt(0); + } + if (type.isEnum()) { + return Enum.valueOf((Class) type, string); + } + if (type == BigInteger.class) { + return new BigInteger(string); + } + if (type == BigDecimal.class) { + return new BigDecimal(string); + } + if (type == Short.class || type == short.class) { + return new Short(string); + } + if (type == Integer.class || type == int.class) { + return new Integer(string); + } + if (type == Long.class || type == long.class) { + return new Long(string); + } + if (type == Double.class || type == double.class) { + return new Double(string); + } + if (type == Float.class || type == float.class) { + return new Float(string); + } + if (type == Byte.class || type == byte.class) { + return new Byte(string); + } + if (type == Boolean.class || type == boolean.class) { + return Boolean.valueOf(string); + } + if (type == Date.class || type == java.sql.Date.class || type == java.sql.Timestamp.class + || type == java.sql.Time.class) { + try { + Date date = new SimpleDateFormat(DATE_FORMAT).parse(string); + if (type == java.sql.Date.class) { + return new java.sql.Date(date.getTime()); + } + if (type == java.sql.Timestamp.class) { + return new java.sql.Timestamp(date.getTime()); + } + if (type == java.sql.Time.class) { + return new java.sql.Time(date.getTime()); + } + return date; + } catch (ParseException e) { + throw new IllegalStateException("Failed to parse date " + value + " by format " + + DATE_FORMAT + ", cause: " + e.getMessage(), e); + } + } + if (type == java.time.LocalDateTime.class) { + if (StringUtils.isEmpty(string)) { + return null; + } + return LocalDateTime.parse(string); + } + if (type == java.time.LocalDate.class) { + if (StringUtils.isEmpty(string)) { + return null; + } + return java.time.LocalDate.parse(string); + } + if (type == java.time.LocalTime.class) { + if (StringUtils.isEmpty(string)) { + return null; + } + return LocalDateTime.parse(string).toLocalTime(); + } + if (type == Class.class) { + try { + return ReflectUtils.name2class(string); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + if (char[].class.equals(type)) { + // Process string to char array for generic invoke + // See + // - https://github.com/apache/dubbo/issues/2003 + int len = string.length(); + char[] chars = new char[len]; + string.getChars(0, len, chars, 0); + return chars; + } + } + if (value instanceof Number) { + Number number = (Number) value; + if (type == byte.class || type == Byte.class) { + return number.byteValue(); + } + if (type == short.class || type == Short.class) { + return number.shortValue(); + } + if (type == int.class || type == Integer.class) { + return number.intValue(); + } + if (type == long.class || type == Long.class) { + return number.longValue(); + } + if (type == float.class || type == Float.class) { + return number.floatValue(); + } + if (type == double.class || type == Double.class) { + return number.doubleValue(); + } + if (type == BigInteger.class) { + return BigInteger.valueOf(number.longValue()); + } + if (type == BigDecimal.class) { + return BigDecimal.valueOf(number.doubleValue()); + } + if (type == Date.class) { + return new Date(number.longValue()); + } + if (type == boolean.class || type == Boolean.class) { + return 0 != number.intValue(); + } + } + if (value instanceof Collection) { + Collection collection = (Collection) value; + if (type.isArray()) { + int length = collection.size(); + Object array = Array.newInstance(type.getComponentType(), length); + int i = 0; + for (Object item : collection) { + Array.set(array, i++, item); + } + return array; + } + if (!type.isInterface()) { + try { + Collection result = (Collection) type.newInstance(); + result.addAll(collection); + return result; + } catch (Throwable ignored) { + } + } + if (type == List.class) { + return new ArrayList(collection); + } + if (type == Set.class) { + return new HashSet(collection); + } + } + if (value.getClass().isArray() && Collection.class.isAssignableFrom(type)) { + int length = Array.getLength(value); + Collection collection; + if (!type.isInterface()) { + try { + collection = (Collection) type.newInstance(); + } catch (Throwable e) { + collection = new ArrayList(length); + } + } else if (type == Set.class) { + collection = new HashSet(Math.max((int) (length/.75f) + 1, 16)); + } else { + collection = new ArrayList(length); + } + for (int i = 0; i < length; i++) { + collection.add(Array.get(value, i)); + } + return collection; + } + return value; + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java index 93a2a60d7d6..97f13a5f96b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ExecutorUtil.java @@ -60,9 +60,7 @@ public static void gracefulShutdown(Executor executor, int timeout) { try { // Disable new tasks from being submitted es.shutdown(); - } catch (SecurityException ex2) { - return; - } catch (NullPointerException ex2) { + } catch (SecurityException | NullPointerException ex2) { return; } try { @@ -86,9 +84,7 @@ public static void shutdownNow(Executor executor, final int timeout) { final ExecutorService es = (ExecutorService) executor; try { es.shutdownNow(); - } catch (SecurityException ex2) { - return; - } catch (NullPointerException ex2) { + } catch (SecurityException | NullPointerException ex2) { return; } try { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java index a6192390a92..a6f32f9d4c6 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java @@ -95,16 +95,13 @@ static Object getFieldValue(Object object, String fieldName) { * @return the value of the specified {@link Field} */ static T getFieldValue(Object object, Field field) { - boolean accessible = field.isAccessible(); Object value = null; try { - if (!accessible) { - field.setAccessible(true); - } + ReflectUtils.makeAccessible(field); value = field.get(object); } catch (IllegalAccessException ignored) { } finally { - field.setAccessible(accessible); + ReflectUtils.makeAccessible(field); } return (T) value; } @@ -130,17 +127,14 @@ static T setFieldValue(Object object, String fieldName, T value) { * @return the previous value of the specified {@link Field} */ static T setFieldValue(Object object, Field field, T value) { - boolean accessible = field.isAccessible(); Object previousValue = null; try { - if (!accessible) { - field.setAccessible(true); - } + ReflectUtils.makeAccessible(field); previousValue = field.get(object); field.set(object, value); } catch (IllegalAccessException ignored) { } finally { - field.setAccessible(accessible); + ReflectUtils.makeAccessible(field); } return (T) previousValue; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java index 02ec00bdcab..1e44924f7f7 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/IOUtils.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common.utils; +import org.apache.dubbo.common.constants.CommonConstants; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -30,6 +31,9 @@ import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +import java.io.FileNotFoundException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -228,4 +232,38 @@ public static void appendLines(File file, String[] lines) throws IOException { writeLines(new FileOutputStream(file, true), lines); } + + /** + * use like spring code + * @param resourceLocation + * @return + */ + public static URL getURL(String resourceLocation) throws FileNotFoundException { + Assert.notNull(resourceLocation, "Resource location must not be null"); + if (resourceLocation.startsWith(CommonConstants.CLASSPATH_URL_PREFIX)) { + String path = resourceLocation.substring(CommonConstants.CLASSPATH_URL_PREFIX.length()); + ClassLoader cl = ClassUtils.getClassLoader(); + URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path)); + if (url == null) { + String description = "class path resource [" + path + "]"; + throw new FileNotFoundException(description + + " cannot be resolved to URL because it does not exist"); + } + return url; + } + try { + // try URL + return new URL(resourceLocation); + } + catch (MalformedURLException ex) { + // no URL -> treat as file path + try { + return new File(resourceLocation).toURI().toURL(); + } + catch (MalformedURLException ex2) { + throw new FileNotFoundException("Resource location [" + resourceLocation + + "] is neither a URL not a well-formed file path"); + } + } + } } \ No newline at end of file diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java index a4c373239fb..8230bd68864 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LFUCache.java @@ -30,12 +30,12 @@ public class LFUCache { private int curSize = 0; private final ReentrantLock lock = new ReentrantLock(); - private static final int DEFAULT_LOAD_FACTOR = 1000; + private static final int DEFAULT_INITIAL_CAPACITY = 1000; - private static final float DEFAULT_EVICTION_CAPACITY = 0.75f; + private static final float DEFAULT_EVICTION_FACTOR = 0.75f; public LFUCache() { - this(DEFAULT_LOAD_FACTOR, DEFAULT_EVICTION_CAPACITY); + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_EVICTION_FACTOR); } /** @@ -46,12 +46,13 @@ public LFUCache() { * @param maxCapacity cache max capacity * @param evictionFactor cache proceedEviction factor */ + @SuppressWarnings("unchecked") public LFUCache(final int maxCapacity, final float evictionFactor) { if (maxCapacity <= 0) { throw new IllegalArgumentException("Illegal initial capacity: " + maxCapacity); } - boolean factorInRange = evictionFactor <= 1 || evictionFactor < 0; + boolean factorInRange = evictionFactor <= 1 && evictionFactor > 0; if (!factorInRange || Float.isNaN(evictionFactor)) { throw new IllegalArgumentException("Illegal eviction factor value:" + evictionFactor); @@ -61,7 +62,7 @@ public LFUCache(final int maxCapacity, final float evictionFactor) { this.map = new HashMap<>(); this.freqTable = new CacheDeque[capacity + 1]; for (int i = 0; i <= capacity; i++) { - freqTable[i] = new CacheDeque(); + freqTable[i] = new CacheDeque<>(); } for (int i = 0; i < capacity; i++) { freqTable[i].nextDeque = freqTable[i + 1]; @@ -77,11 +78,9 @@ public V put(final K key, final V value) { CacheNode node; lock.lock(); try { - if (map.containsKey(key)) { - node = map.get(key); - if (node != null) { - CacheNode.withdrawNode(node); - } + node = map.get(key); + if (node != null) { + CacheNode.withdrawNode(node); node.value = value; freqTable[0].addLastNode(node); map.put(key, node); @@ -171,7 +170,7 @@ static class CacheNode { CacheNode next; K key; V value; - CacheDeque owner; + CacheDeque owner; CacheNode() { } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java index 27262b48eae..e381926f86b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java @@ -34,6 +34,7 @@ import static org.apache.dubbo.common.utils.MemberUtils.isStatic; import static org.apache.dubbo.common.utils.ReflectUtils.EMPTY_CLASS_ARRAY; import static org.apache.dubbo.common.utils.ReflectUtils.resolveTypes; +import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty; /** * Miscellaneous method utility methods. @@ -50,7 +51,7 @@ public interface MethodUtils { * @param method the method to check * @return whether the given method is setter method */ - public static boolean isSetter(Method method) { + static boolean isSetter(Method method) { return method.getName().startsWith("set") && !"set".equals(method.getName()) && Modifier.isPublic(method.getModifiers()) @@ -65,7 +66,7 @@ public static boolean isSetter(Method method) { * @param method the method to check * @return whether the given method is getter method */ - public static boolean isGetter(Method method) { + static boolean isGetter(Method method) { String name = method.getName(); return (name.startsWith("get") || name.startsWith("is")) && !"get".equals(name) && !"is".equals(name) @@ -82,7 +83,7 @@ public static boolean isGetter(Method method) { * @param method the method to check * @return whether the given method is meta method */ - public static boolean isMetaMethod(Method method) { + static boolean isMetaMethod(Method method) { String name = method.getName(); if (!(name.startsWith("get") || name.startsWith("is"))) { return false; @@ -113,12 +114,11 @@ public static boolean isMetaMethod(Method method) { * @param method the method to check * @return whether the given method is deprecated method */ - public static boolean isDeprecated(Method method) { + static boolean isDeprecated(Method method) { return method.getAnnotation(Deprecated.class) != null; } - /** * Create an instance of {@link Predicate} for {@link Method} to exclude the specified declared class * @@ -252,7 +252,9 @@ static Method findMethod(Class type, String methodName) { static Method findMethod(Class type, String methodName, Class... parameterTypes) { Method method = null; try { - method = type.getDeclaredMethod(methodName, parameterTypes); + if (type != null && isNotEmpty(methodName)) { + method = type.getDeclaredMethod(methodName, parameterTypes); + } } catch (NoSuchMethodException e) { } return method; @@ -275,13 +277,8 @@ static T invokeMethod(Object object, String methodName, Object... methodPara T value = null; try { - final boolean isAccessible = method.isAccessible(); - - if (!isAccessible) { - method.setAccessible(true); - } + ReflectUtils.makeAccessible(method); value = (T) method.invoke(object, methodParameters); - method.setAccessible(isAccessible); } catch (Exception e) { throw new IllegalArgumentException(e); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java index 1845514ba8b..b10e6cb2091 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java @@ -471,10 +471,8 @@ public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddre public static void setInterface(MulticastSocket multicastSocket, boolean preferIpv6) throws IOException { boolean interfaceSet = false; - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface i = (NetworkInterface) interfaces.nextElement(); - Enumeration addresses = i.getInetAddresses(); + for (NetworkInterface networkInterface : getValidNetworkInterfaces()) { + Enumeration addresses = networkInterface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = (InetAddress) addresses.nextElement(); if (preferIpv6 && address instanceof Inet6Address) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java index 2a6b73ed0be..aa78a1ed99f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java @@ -23,6 +23,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -30,6 +31,8 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -41,6 +44,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Properties; import java.util.TreeMap; import java.util.WeakHashMap; @@ -50,6 +54,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; + /** * PojoUtils. Travel object deeply, and convert complex type to simple type. *

    @@ -68,6 +74,8 @@ public class PojoUtils { private static final ConcurrentMap NAME_METHODS_CACHE = new ConcurrentHashMap(); private static final ConcurrentMap, ConcurrentMap> CLASS_FIELD_CACHE = new ConcurrentHashMap, ConcurrentMap>(); private static final boolean GENERIC_WITH_CLZ = Boolean.parseBoolean(ConfigUtils.getProperty(CommonConstants.GENERIC_WITH_CLZ_KEY, "true")); + private static final List> CLASS_CAN_BE_STRING = Arrays.asList(Byte.class, Short.class, Integer.class, + Long.class, Float.class, Double.class, Boolean.class, Character.class); public static Object[] generalize(Object[] objs) { Object[] dests = new Object[objs.length]; @@ -173,6 +181,7 @@ private static Object generalize(Object pojo, Map history) { } for (Method method : pojo.getClass().getMethods()) { if (ReflectUtils.isBeanPropertyReadMethod(method)) { + ReflectUtils.makeAccessible(method); try { map.put(ReflectUtils.getPropertyNameFromBeanReadMethod(method), generalize(method.invoke(pojo), history)); } catch (Exception e) { @@ -247,7 +256,7 @@ private static Collection createCollection(Class type, int len) { return new ArrayList(len); } if (type.isAssignableFrom(HashSet.class)) { - return new HashSet(len); + return new HashSet(Math.max((int) (len/.75f) + 1, 16)); } if (!type.isInterface() && !Modifier.isAbstract(type.getModifiers())) { try { @@ -256,18 +265,19 @@ private static Collection createCollection(Class type, int len) { // ignore } } - return new ArrayList(); + return new ArrayList(len); } private static Map createMap(Map src) { Class cl = src.getClass(); + int size = src.size(); Map result = null; if (HashMap.class == cl) { - result = new HashMap(); + result = new HashMap(Math.max((int) (size/.75f) + 1, 16)); } else if (Hashtable.class == cl) { - result = new Hashtable(); + result = new Hashtable(Math.max((int) (size/.75f) + 1, 16)); } else if (IdentityHashMap.class == cl) { - result = new IdentityHashMap(); + result = new IdentityHashMap((int)(1 + size * 1.1)); } else if (LinkedHashMap.class == cl) { result = new LinkedHashMap(); } else if (Properties.class == cl) { @@ -275,9 +285,9 @@ private static Map createMap(Map src) { } else if (TreeMap.class == cl) { result = new TreeMap(); } else if (WeakHashMap.class == cl) { - return new WeakHashMap(); + return new WeakHashMap(Math.max((int) (size/.75f) + 1, 16)); } else if (ConcurrentHashMap.class == cl) { - result = new ConcurrentHashMap(); + result = new ConcurrentHashMap(Math.max((int) (size/.75f) + 1, 16)); } else if (ConcurrentSkipListMap.class == cl) { result = new ConcurrentSkipListMap(); } else { @@ -294,7 +304,7 @@ private static Map createMap(Map src) { } if (result == null) { - result = new HashMap(); + result = new HashMap(Math.max((int) (size/.75f) + 1, 16)); } return result; @@ -384,10 +394,15 @@ private static Object realize0(Object pojo, Class type, Type genericType, fin } if (pojo instanceof Map && type != null) { + Map map = (Map) pojo; Object className = ((Map) pojo).get("class"); if (className instanceof String) { + SerializeClassChecker.getInstance().validateClass((String) className); try { type = ClassUtils.forName((String) className); + if (GENERIC_WITH_CLZ) { + map.remove("class"); + } } catch (ClassNotFoundException e) { // ignore } @@ -400,23 +415,6 @@ private static Object realize0(Object pojo, Class type, Type genericType, fin return Enum.valueOf((Class) type, name.toString()); } } - Map map; - // when return type is not the subclass of return type from the signature and not an interface - if (!type.isInterface() && !type.isAssignableFrom(pojo.getClass())) { - try { - map = (Map) type.newInstance(); - Map mapPojo = (Map) pojo; - map.putAll(mapPojo); - if (GENERIC_WITH_CLZ) { - map.remove("class"); - } - } catch (Exception e) { - //ignore error - map = (Map) pojo; - } - } else { - map = (Map) pojo; - } if (Map.class.isAssignableFrom(type) || type == Object.class) { final Map result; @@ -472,11 +470,7 @@ private static Object realize0(Object pojo, Class type, Type genericType, fin Object value = entry.getValue(); if (value != null) { Method method = getSetterMethod(dest.getClass(), name, value.getClass()); - Field field = getField(dest.getClass(), name); if (method != null) { - if (!method.isAccessible()) { - method.setAccessible(true); - } Type ptype = method.getGenericParameterTypes()[0]; value = realize0(value, method.getParameterTypes()[0], ptype, history); try { @@ -487,12 +481,16 @@ private static Object realize0(Object pojo, Class type, Type genericType, fin logger.error(exceptionDescription, e); throw new RuntimeException(exceptionDescription, e); } - } else if (field != null) { - value = realize0(value, field.getType(), field.getGenericType(), history); - try { - field.set(dest, value); - } catch (IllegalAccessException e) { - throw new RuntimeException("Failed to set field " + name + " of pojo " + dest.getClass().getName() + " : " + e.getMessage(), e); + } else { + Field field = getField(dest.getClass(), name); + if (field != null) { + value = realize0(value, field.getType(), field.getGenericType(), history); + try { + field.set(dest, value); + } catch (IllegalAccessException e) { + String exceptionDescription = "Failed to set field " + name + " of pojo " + dest.getClass().getName() + " : " + e.getMessage(); + throw new RuntimeException(exceptionDescription, e); + } } } } @@ -503,9 +501,7 @@ private static Object realize0(Object pojo, Class type, Type genericType, fin if (message instanceof String) { try { Field field = Throwable.class.getDeclaredField("detailMessage"); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtils.makeAccessible(field); field.set(dest, message); } catch (Exception e) { } @@ -584,14 +580,10 @@ private static Object newInstance(Class cls) { } } } - constructor.setAccessible(true); + ReflectUtils.makeAccessible(constructor); Object[] parameters = Arrays.stream(constructor.getParameterTypes()).map(PojoUtils::getDefaultValue).toArray(); return constructor.newInstance(parameters); - } catch (InstantiationException e) { - throw new RuntimeException(e.getMessage(), e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e.getMessage(), e); - } catch (InvocationTargetException e) { + } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } } @@ -607,9 +599,15 @@ private static Object getDefaultValue(Class parameterType) { if ("char".equals(parameterType.getName())) { return Character.MIN_VALUE; } - if ("bool".equals(parameterType.getName())) { + if ("boolean".equals(parameterType.getName())) { return false; } + if ("byte".equals(parameterType.getName())) { + return (byte) 0; + } + if ("short".equals(parameterType.getName())) { + return (short) 0; + } return parameterType.isPrimitive() ? 0 : null; } @@ -628,6 +626,7 @@ private static Method getSetterMethod(Class cls, String property, Class va } } if (method != null) { + ReflectUtils.makeAccessible(method); NAME_METHODS_CACHE.put(cls.getName() + "." + name + "(" + valueCls.getName() + ")", method); } } @@ -641,7 +640,7 @@ private static Field getField(Class cls, String fieldName) { } try { result = cls.getDeclaredField(fieldName); - result.setAccessible(true); + ReflectUtils.makeAccessible(result); } catch (NoSuchFieldException e) { for (Field field : cls.getFields()) { if (fieldName.equals(field.getName()) && ReflectUtils.isPublicInstanceField(field)) { @@ -678,4 +677,93 @@ public static void updatePropertyIfAbsent(Supplier getterMethod, Consumer } } + /** + * convert map to a specific class instance + * + * @param map map wait for convert + * @param cls the specified class + * @param the type of {@code cls} + * @return class instance declare in param {@code cls} + * @throws ReflectiveOperationException if the instance creation is failed + * @since 2.7.10 + */ + public static T mapToPojo(Map map, Class cls) throws ReflectiveOperationException { + T instance = cls.getDeclaredConstructor().newInstance(); + Map beanPropertyFields = ReflectUtils.getBeanPropertyFields(cls); + for (Map.Entry entry : beanPropertyFields.entrySet()) { + String name = entry.getKey(); + Field field = entry.getValue(); + Object mapObject = map.get(name); + if (mapObject == null) { + continue; + } + + Type type = field.getGenericType(); + Object fieldObject = getFieldObject(mapObject, type); + field.set(instance, fieldObject); + } + + return instance; + } + + private static Object getFieldObject(Object mapObject, Type fieldType) throws ReflectiveOperationException { + if (fieldType instanceof Class) { + return convertClassType(mapObject, (Class) fieldType); + } else if (fieldType instanceof ParameterizedType) { + return convertParameterizedType(mapObject, (ParameterizedType) fieldType); + } else if (fieldType instanceof GenericArrayType || fieldType instanceof TypeVariable || fieldType instanceof WildcardType) { + // ignore these type currently + return null; + } else { + throw new IllegalArgumentException("Unrecognized Type: " + fieldType.toString()); + } + } + + @SuppressWarnings("unchecked") + private static Object convertClassType(Object mapObject, Class type) throws ReflectiveOperationException { + if (type.isPrimitive() || isAssignableFrom(type, mapObject.getClass())) { + return mapObject; + } else if (Objects.equals(type, String.class) && CLASS_CAN_BE_STRING.contains(mapObject.getClass())) { + // auto convert specified type to string + return mapObject.toString(); + } else if (mapObject instanceof Map) { + return mapToPojo((Map) mapObject, type); + } else { + // type didn't match and mapObject is not another Map struct. + // we just ignore this situation. + return null; + } + } + + @SuppressWarnings("unchecked") + private static Object convertParameterizedType(Object mapObject, ParameterizedType type) throws ReflectiveOperationException { + Type rawType = type.getRawType(); + if (!isAssignableFrom((Class) rawType, mapObject.getClass())) { + return null; + } + + Type[] actualTypeArguments = type.getActualTypeArguments(); + if (isAssignableFrom(Map.class, (Class) rawType)) { + Map map = (Map) mapObject.getClass().getDeclaredConstructor().newInstance(); + for (Map.Entry entry : ((Map) mapObject).entrySet()) { + Object key = getFieldObject(entry.getKey(), actualTypeArguments[0]); + Object value = getFieldObject(entry.getValue(), actualTypeArguments[1]); + map.put(key, value); + } + + return map; + } else if (isAssignableFrom(Collection.class, (Class) rawType)) { + Collection collection = (Collection) mapObject.getClass().getDeclaredConstructor().newInstance(); + for (Object m : (Iterable) mapObject) { + Object ele = getFieldObject(m, actualTypeArguments[0]); + collection.add(ele); + } + + return collection; + } else { + // ignore other type currently + return null; + } + } + } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java index ea8d51f7640..f1fc246ac8a 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java @@ -31,6 +31,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; @@ -154,8 +155,8 @@ private ReflectUtils() { } public static boolean isPrimitives(Class cls) { - if (cls.isArray()) { - return isPrimitive(cls.getComponentType()); + while (cls.isArray()) { + cls = cls.getComponentType(); } return isPrimitive(cls); } @@ -904,7 +905,9 @@ private static Class[] desc2classArray(ClassLoader cl, String desc) throws Cl * @throws NoSuchMethodException * @throws ClassNotFoundException * @throws IllegalStateException when multiple methods are found (overridden method when parameter info is not provided) + * @deprecated Recommend {@link MethodUtils#findMethod(Class, String, Class[])} */ + @Deprecated public static Method findMethodByMethodSignature(Class clazz, String methodName, String[] parameterTypes) throws NoSuchMethodException, ClassNotFoundException { String signature = clazz.getName() + "." + methodName; @@ -943,6 +946,16 @@ public static Method findMethodByMethodSignature(Class clazz, String methodNa return method; } + /** + * @param clazz Target class to find method + * @param methodName Method signature, e.g.: method1(int, String). It is allowed to provide method name only, e.g.: method2 + * @return target method + * @throws NoSuchMethodException + * @throws ClassNotFoundException + * @throws IllegalStateException when multiple methods are found (overridden method when parameter info is not provided) + * @deprecated Recommend {@link MethodUtils#findMethod(Class, String, Class[])} + */ + @Deprecated public static Method findMethodByMethodName(Class clazz, String methodName) throws NoSuchMethodException, ClassNotFoundException { return findMethodByMethodSignature(clazz, methodName, null); @@ -1051,7 +1064,7 @@ private static Object getEmptyObject(Class returnType, Map, Object> try { Object value = emptyInstances.get(returnType); if (value == null) { - value = returnType.newInstance(); + value = returnType.getDeclaredConstructor().newInstance(); emptyInstances.put(returnType, value); } Class cls = value.getClass(); @@ -1064,9 +1077,7 @@ private static Object getEmptyObject(Class returnType, Map, Object> Object property = getEmptyObject(field.getType(), emptyInstances, level + 1); if (property != null) { try { - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtils.makeAccessible(field); field.set(value, property); } catch (Throwable ignored) { } @@ -1155,8 +1166,7 @@ public static Map getBeanPropertyFields(Class cl) { || Modifier.isStatic(field.getModifiers())) { continue; } - - field.setAccessible(true); + ReflectUtils.makeAccessible(field); properties.put(field.getName(), field); } @@ -1171,7 +1181,7 @@ public static Map getBeanPropertyReadMethods(Class cl) { Method[] methods = cl.getDeclaredMethods(); for (Method method : methods) { if (isBeanPropertyReadMethod(method)) { - method.setAccessible(true); + ReflectUtils.makeAccessible(method); String property = getPropertyNameFromBeanReadMethod(method); properties.put(property, method); } @@ -1190,6 +1200,9 @@ public static Type[] getReturnTypes(Method method) { if (actualArgType instanceof ParameterizedType) { returnType = (Class) ((ParameterizedType) actualArgType).getRawType(); genericReturnType = actualArgType; + } else if (actualArgType instanceof TypeVariable) { + returnType = (Class) ((TypeVariable) actualArgType).getBounds()[0]; + genericReturnType = actualArgType; } else { returnType = (Class) actualArgType; genericReturnType = returnType; @@ -1217,16 +1230,14 @@ public static Set findParameterizedTypes(Class sourceClass Set parameterizedTypes = genericTypes.stream() .filter(type -> type instanceof ParameterizedType)// filter ParameterizedType - .map(type -> ParameterizedType.class.cast(type)) // cast to ParameterizedType + .map(ParameterizedType.class::cast) // cast to ParameterizedType .collect(Collectors.toSet()); if (parameterizedTypes.isEmpty()) { // If not found, try to search super types recursively genericTypes.stream() .filter(type -> type instanceof Class) - .map(type -> Class.class.cast(type)) - .forEach(superClass -> { - parameterizedTypes.addAll(findParameterizedTypes(superClass)); - }); + .map(Class.class::cast) + .forEach(superClass -> parameterizedTypes.addAll(findParameterizedTypes(superClass))); } return unmodifiableSet(parameterizedTypes); // build as a Set @@ -1316,4 +1327,69 @@ public static Class[] resolveTypes(Object... values) { return types; } -} \ No newline at end of file + + /** + * Copy from org.springframework.util.ReflectionUtils. + * Make the given method accessible, explicitly setting it accessible if + * necessary. The {@code setAccessible(true)} method is only called + * when actually necessary, to avoid unnecessary conflicts with a JVM + * SecurityManager (if active). + * @param method the method to make accessible + * @see java.lang.reflect.Method#setAccessible + */ + @SuppressWarnings("deprecation") // on JDK 9 + public static void makeAccessible(Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || + !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { + method.setAccessible(true); + } + } + + /** + * Copy from org.springframework.util.ReflectionUtils. + * Make the given field accessible, explicitly setting it accessible if + * necessary. The {@code setAccessible(true)} method is only called + * when actually necessary, to avoid unnecessary conflicts with a JVM + * SecurityManager (if active). + * @param field the field to make accessible + * @see java.lang.reflect.Field#setAccessible + */ + @SuppressWarnings("deprecation") // on JDK 9 + public static void makeAccessible(Field field) { + if ((!Modifier.isPublic(field.getModifiers()) || + !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || + Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + /** + * Copy from org.springframework.util.ReflectionUtils. + * Make the given constructor accessible, explicitly setting it accessible + * if necessary. The {@code setAccessible(true)} method is only called + * when actually necessary, to avoid unnecessary conflicts with a JVM + * SecurityManager (if active). + * @param ctor the constructor to make accessible + * @see java.lang.reflect.Constructor#setAccessible + */ + @SuppressWarnings("deprecation") // on JDK 9 + public static void makeAccessible(Constructor ctor) { + if ((!Modifier.isPublic(ctor.getModifiers()) || + !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) { + ctor.setAccessible(true); + } + } + + public static boolean checkZeroArgConstructor(Class clazz) { + try { + clazz.getDeclaredConstructor(); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + public static boolean isJdk(Class clazz) { + return clazz.getName().startsWith("java.") || clazz.getName().startsWith("javax."); + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java new file mode 100644 index 00000000000..b75ae65a0ec --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.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.dubbo.common.utils; + +import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +public class SerializeClassChecker { + private static final Logger logger = LoggerFactory.getLogger(SerializeClassChecker.class); + + private static volatile SerializeClassChecker INSTANCE = null; + + private final boolean BLOCK_ALL_CLASS_EXCEPT_ALLOW; + private final Set CLASS_DESERIALIZE_ALLOWED_SET = new ConcurrentHashSet<>(); + private final Set CLASS_DESERIALIZE_BLOCKED_SET = new ConcurrentHashSet<>(); + + private final Object CACHE = new Object(); + private final LFUCache CLASS_ALLOW_LFU_CACHE = new LFUCache<>(); + private final LFUCache CLASS_BLOCK_LFU_CACHE = new LFUCache<>(); + + private final AtomicLong counter = new AtomicLong(0); + + private SerializeClassChecker() { + String blockAllClassExceptAllow = System.getProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL, "false"); + BLOCK_ALL_CLASS_EXCEPT_ALLOW = Boolean.parseBoolean(blockAllClassExceptAllow); + + String[] lines; + try { + ClassLoader classLoader = ClassUtils.getClassLoader(JavaBeanSerializeUtil.class); + if (classLoader != null) { + lines = IOUtils.readLines(classLoader.getResourceAsStream(CommonConstants.SERIALIZE_BLOCKED_LIST_FILE_PATH)); + } else { + lines = IOUtils.readLines(ClassLoader.getSystemResourceAsStream(CommonConstants.SERIALIZE_BLOCKED_LIST_FILE_PATH)); + } + for (String line : lines) { + line = line.trim(); + if (StringUtils.isEmpty(line) || line.startsWith("#")) { + continue; + } + CLASS_DESERIALIZE_BLOCKED_SET.add(line); + } + + } catch (IOException e) { + logger.error("Failed to load blocked class list! Will ignore default blocked list.", e); + } + + String allowedClassList = System.getProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT); + String blockedClassList = System.getProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST, "").trim().toLowerCase(Locale.ROOT); + + if (StringUtils.isNotEmpty(allowedClassList)) { + String[] classStrings = allowedClassList.trim().split(","); + CLASS_DESERIALIZE_ALLOWED_SET.addAll(Arrays.asList(classStrings)); + } + + if (StringUtils.isNotEmpty(blockedClassList)) { + String[] classStrings = blockedClassList.trim().split(","); + CLASS_DESERIALIZE_BLOCKED_SET.addAll(Arrays.asList(classStrings)); + } + + } + + public static SerializeClassChecker getInstance() { + if (INSTANCE == null) { + synchronized (SerializeClassChecker.class) { + if (INSTANCE == null) { + INSTANCE = new SerializeClassChecker(); + } + } + } + return INSTANCE; + } + + /** + * For ut only + */ + @Deprecated + protected static void clearInstance() { + INSTANCE = null; + } + + /** + * Check if a class is in block list, using prefix match + * + * @throws IllegalArgumentException if class is blocked + * @param name class name ( all are convert to lower case ) + */ + public void validateClass(String name) { + name = name.toLowerCase(Locale.ROOT); + if (CACHE == CLASS_ALLOW_LFU_CACHE.get(name)) { + return; + } + + if (CACHE == CLASS_BLOCK_LFU_CACHE.get(name)) { + error(name); + } + + for (String allowedPrefix : CLASS_DESERIALIZE_ALLOWED_SET) { + if (name.startsWith(allowedPrefix)) { + CLASS_ALLOW_LFU_CACHE.put(name, CACHE); + return; + } + } + + for (String blockedPrefix : CLASS_DESERIALIZE_BLOCKED_SET) { + if (BLOCK_ALL_CLASS_EXCEPT_ALLOW || name.startsWith(blockedPrefix)) { + CLASS_BLOCK_LFU_CACHE.put(name, CACHE); + error(name); + } + } + + CLASS_ALLOW_LFU_CACHE.put(name, CACHE); + } + + private void error(String name) { + String notice = "Trigger the safety barrier! " + + "Catch not allowed serialize class. " + + "Class name: " + name + " . " + + "This means currently maybe being attacking by others." + + "If you are sure this is a mistake, " + + "please add this class name to `" + CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST + + "` as a system environment property."; + if (counter.incrementAndGet() % 1000 == 0 || counter.get() < 100) { + logger.error(notice); + } + throw new IllegalArgumentException(notice); + } + +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java index 58e5e7a9b4b..c49b3a368c8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ServiceAnnotationResolver.java @@ -16,11 +16,15 @@ */ package org.apache.dubbo.common.utils; +import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Service; import java.lang.annotation.Annotation; +import java.util.List; import static java.lang.String.format; +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute; import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty; import static org.apache.dubbo.common.utils.ClassUtils.isGenericClass; @@ -36,6 +40,13 @@ */ public class ServiceAnnotationResolver { + /** + * The annotation {@link Class classes} of Dubbo Service (read-only) + * + * @since 2.7.9 + */ + public static List> SERVICE_ANNOTATION_CLASSES = unmodifiableList(asList(DubboService.class, Service.class, com.alibaba.dubbo.config.annotation.Service.class)); + private final Annotation serviceAnnotation; private final Class serviceType; @@ -47,16 +58,18 @@ public ServiceAnnotationResolver(Class serviceType) throws IllegalArgumentExc private Annotation getServiceAnnotation(Class serviceType) { - Annotation serviceAnnotation = serviceType.getAnnotation(Service.class); + Annotation serviceAnnotation = null; - if (serviceAnnotation == null) { - serviceAnnotation = serviceType.getAnnotation(com.alibaba.dubbo.config.annotation.Service.class); + for (Class serviceAnnotationClass : SERVICE_ANNOTATION_CLASSES) { + serviceAnnotation = serviceType.getAnnotation(serviceAnnotationClass); + if (serviceAnnotation != null) { + break; + } } if (serviceAnnotation == null) { - throw new IllegalArgumentException(format("@%s or @%s can't be found in the service type[%s].", - Service.class.getName(), - com.alibaba.dubbo.config.annotation.Service.class.getName(), + throw new IllegalArgumentException(format("Any annotation of [%s] can't be annotated in the service type[%s].", + SERVICE_ANNOTATION_CLASSES, serviceType.getName() )); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java index fc444750c57..05c788ec369 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java @@ -43,7 +43,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; import static org.apache.dubbo.common.constants.CommonConstants.DOT_REGEX; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; +import static org.apache.dubbo.common.constants.CommonConstants.HIDDEN_KEY_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SEPARATOR_REGEX; import static org.apache.dubbo.common.constants.CommonConstants.UNDERLINE_SEPARATOR; @@ -940,7 +940,7 @@ public static String trim(String str) { } public static String toURLKey(String key) { - return key.toLowerCase().replaceAll(SEPARATOR_REGEX, HIDE_KEY_PREFIX); + return key.toLowerCase().replaceAll(SEPARATOR_REGEX, HIDDEN_KEY_PREFIX); } public static String toOSStyleKey(String key) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java index d45ada1fe55..7b17515199b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java @@ -197,11 +197,7 @@ public static Map> convertRegister(Map 0) { name = name + ":" + version; } - Map newUrls = newRegister.get(name); - if (newUrls == null) { - newUrls = new HashMap(); - newRegister.put(name, newUrls); - } + Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap<>()); newUrls.put(serviceUrl, StringUtils.toQueryString(params)); } } else { @@ -258,11 +254,7 @@ public static Map> revertRegister(Map newUrls = newRegister.get(name); - if (newUrls == null) { - newUrls = new HashMap(); - newRegister.put(name, newUrls); - } + Map newUrls = newRegister.computeIfAbsent(name, k -> new HashMap()); newUrls.put(serviceUrl, StringUtils.toQueryString(params)); } } else { @@ -321,11 +313,7 @@ public static Map> revertNotify(Map 0) { name = name + ":" + version; } - Map newUrls = newNotify.get(name); - if (newUrls == null) { - newUrls = new HashMap(); - newNotify.put(name, newUrls); - } + Map newUrls = newNotify.computeIfAbsent(name, k -> new HashMap()); newUrls.put(url, StringUtils.toQueryString(params)); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java index db2b583c22c..988ab1ba7b7 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java @@ -519,7 +519,7 @@ public String toString() { buf.append(" "); buf.append(key); buf.append("=\""); - buf.append(value); + buf.append(key.equals("password") ? "******" : value); buf.append("\""); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java index 970a6aec130..536286c17b3 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java @@ -26,6 +26,7 @@ import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ServiceMetadata; import java.util.ArrayList; import java.util.Arrays; @@ -52,6 +53,21 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig { private static final long serialVersionUID = -1559314110797223229L; + /** + * The interface name of the exported service + */ + protected String interfaceName; + /** + * The remote service version the customer/provider side will reference + */ + protected String version; + + /** + * The remote service group the customer/provider side will reference + */ + protected String group; + + protected ServiceMetadata serviceMetadata; /** * Local impl class name for the service interface */ @@ -312,6 +328,10 @@ private void convertRegistryIdsToRegistries() { } + protected boolean notHasSelfRegistryProperty() { + return CollectionUtils.isEmpty(registries) && StringUtils.isEmpty(registryIds); + } + public void completeCompoundConfigs(AbstractInterfaceConfig interfaceConfig) { if (interfaceConfig != null) { if (application == null) { @@ -320,15 +340,16 @@ public void completeCompoundConfigs(AbstractInterfaceConfig interfaceConfig) { if (module == null) { setModule(interfaceConfig.getModule()); } - if (registries == null) { + if (notHasSelfRegistryProperty()) { setRegistries(interfaceConfig.getRegistries()); + setRegistryIds(interfaceConfig.getRegistryIds()); } if (monitor == null) { setMonitor(interfaceConfig.getMonitor()); } } if (module != null) { - if (registries == null) { + if (notHasSelfRegistryProperty()) { setRegistries(module.getRegistries()); } if (monitor == null) { @@ -336,8 +357,9 @@ public void completeCompoundConfigs(AbstractInterfaceConfig interfaceConfig) { } } if (application != null) { - if (registries == null) { + if (notHasSelfRegistryProperty()) { setRegistries(application.getRegistries()); + setRegistryIds(application.getRegistryIds()); } if (monitor == null) { setMonitor(application.getMonitor()); @@ -346,10 +368,9 @@ public void completeCompoundConfigs(AbstractInterfaceConfig interfaceConfig) { } protected void computeValidRegistryIds() { - if (StringUtils.isEmpty(getRegistryIds())) { - if (getApplication() != null && StringUtils.isNotEmpty(getApplication().getRegistryIds())) { - setRegistryIds(getApplication().getRegistryIds()); - } + if (application != null && notHasSelfRegistryProperty()) { + setRegistries(application.getRegistries()); + setRegistryIds(application.getRegistryIds()); } } @@ -451,11 +472,18 @@ public void setLayer(String layer) { this.layer = layer; } + /** + * Always use the global ApplicationConfig + */ public ApplicationConfig getApplication() { - if (application != null) { + ApplicationConfig globalApplication = ApplicationModel.getConfigManager().getApplicationOrElseThrow(); + if (globalApplication == null) { + return application; + } + if (application != null && !StringUtils.isEquals(application.getName(), globalApplication.getName())) { return application; } - return ApplicationModel.getConfigManager().getApplicationOrElseThrow(); + return globalApplication; } @Deprecated @@ -685,4 +713,46 @@ public void setAuth(Boolean auth) { public SslConfig getSslConfig() { return ApplicationModel.getConfigManager().getSsl().orElse(null); } + + public void initServiceMetadata(AbstractInterfaceConfig interfaceConfig) { + serviceMetadata.setVersion(getVersion(interfaceConfig)); + serviceMetadata.setGroup(getGroup(interfaceConfig)); + serviceMetadata.setDefaultGroup(getGroup(interfaceConfig)); + serviceMetadata.setServiceInterfaceName(getInterface()); + } + + public String getGroup(AbstractInterfaceConfig interfaceConfig) { + return StringUtils.isEmpty(this.group) ? (interfaceConfig != null ? interfaceConfig.getGroup() : this.group) : this.group; + } + + public String getVersion(AbstractInterfaceConfig interfaceConfig) { + return StringUtils.isEmpty(this.version) ? (interfaceConfig != null ? interfaceConfig.getVersion() : this.version) : this.version; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getInterface() { + return interfaceName; + } + + public void setInterface(String interfaceName) { + this.interfaceName = interfaceName; +// if (StringUtils.isEmpty(id)) { +// id = interfaceName; +// } + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java index 27d4ddc9a02..f2038a1117f 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java @@ -71,15 +71,7 @@ public abstract class AbstractReferenceConfig extends AbstractInterfaceConfig { //TODO solve merge problem protected Boolean stubevent;//= Constants.DEFAULT_STUB_EVENT; - /** - * The remote service version the customer side will reference - */ - protected String version; - /** - * The remote service group the customer side will reference - */ - protected String group; /** * declares which app or service this interface belongs to @@ -212,21 +204,7 @@ public void setSticky(Boolean sticky) { this.sticky = sticky; } - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } @Parameter(key = "provided-by") public String getProvidedBy() { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java index e482e3545d5..fe65c5bf5a8 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java @@ -42,6 +42,7 @@ import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST; import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT; +import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PUBLISH_INTERFACE_KEY; import static org.apache.dubbo.config.Constants.DEVELOPMENT_ENVIRONMENT; import static org.apache.dubbo.config.Constants.PRODUCTION_ENVIRONMENT; import static org.apache.dubbo.config.Constants.TEST_ENVIRONMENT; @@ -159,6 +160,8 @@ public class ApplicationConfig extends AbstractConfig { private String repository; + private Boolean publishInterface; + /** * Metadata Service, used in Service Discovery */ @@ -450,6 +453,15 @@ public void setRepository(String repository) { this.repository = repository; } + @Parameter(key = REGISTRY_PUBLISH_INTERFACE_KEY) + public Boolean getPublishInterface() { + return publishInterface; + } + + public void setPublishInterface(Boolean publishInterface) { + this.publishInterface = publishInterface; + } + @Parameter(key = "metadata-service-port") public Integer getMetadataServicePort() { return metadataServicePort; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java index 0e01609aac4..0c3b443bb1d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java @@ -73,7 +73,7 @@ public class ConfigCenterConfig extends AbstractConfig { private String configFile = CommonConstants.DEFAULT_DUBBO_PROPERTIES; /* the .properties file under 'configFile' is global shared while .properties under this one is limited only to this application - */ + */ private String appConfigFile; /* If the Config Center product you use have some special parameters that is not covered by this class, you can add it to here. diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java index 5af0de470fb..659b7bbf563 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/Constants.java @@ -115,4 +115,6 @@ public interface Constants { String ZOOKEEPER_PROTOCOL = "zookeeper"; String REGISTER_KEY = "register"; + + String IGNORE_CHECK_KEYS = "ignoreCheckKeys"; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java index 25cca824790..d61ab2abea2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java @@ -79,6 +79,11 @@ public class ProtocolConfig extends AbstractConfig { */ private Integer iothreads; + /** + * Thread pool keepAliveTime, default unit TimeUnit.MILLISECONDS + */ + private Integer alive; + /** * Thread pool's queue length */ @@ -300,6 +305,14 @@ public void setIothreads(Integer iothreads) { this.iothreads = iothreads; } + public Integer getAlive() { + return alive; + } + + public void setAlive(Integer alive) { + this.alive = alive; + } + public Integer getQueues() { return queues; } @@ -540,4 +553,46 @@ public void refresh() { public boolean isValid() { return StringUtils.isNotEmpty(name); } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ProtocolConfig{"); + sb.append("name='").append(name).append('\''); + sb.append(", host='").append(host).append('\''); + sb.append(", port=").append(port); + sb.append(", contextpath='").append(contextpath).append('\''); + sb.append(", threadpool='").append(threadpool).append('\''); + sb.append(", threadname='").append(threadname).append('\''); + sb.append(", corethreads=").append(corethreads); + sb.append(", threads=").append(threads); + sb.append(", iothreads=").append(iothreads); + sb.append(", alive=").append(alive); + sb.append(", queues=").append(queues); + sb.append(", accepts=").append(accepts); + sb.append(", codec='").append(codec).append('\''); + sb.append(", serialization='").append(serialization).append('\''); + sb.append(", charset='").append(charset).append('\''); + sb.append(", payload=").append(payload); + sb.append(", buffer=").append(buffer); + sb.append(", heartbeat=").append(heartbeat); + sb.append(", accesslog='").append(accesslog).append('\''); + sb.append(", transporter='").append(transporter).append('\''); + sb.append(", exchanger='").append(exchanger).append('\''); + sb.append(", dispatcher='").append(dispatcher).append('\''); + sb.append(", networker='").append(networker).append('\''); + sb.append(", server='").append(server).append('\''); + sb.append(", client='").append(client).append('\''); + sb.append(", telnet='").append(telnet).append('\''); + sb.append(", prompt='").append(prompt).append('\''); + sb.append(", status='").append(status).append('\''); + sb.append(", register=").append(register); + sb.append(", keepAlive=").append(keepAlive); + sb.append(", optimizer='").append(optimizer).append('\''); + sb.append(", extension='").append(extension).append('\''); + sb.append(", parameters=").append(parameters); + sb.append(", isDefault=").append(isDefault); + sb.append(", sslEnabled=").append(sslEnabled); + sb.append('}'); + return sb.toString(); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java index 7b38a75515d..ad948f1b6f4 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ProviderConfig.java @@ -69,6 +69,11 @@ public class ProviderConfig extends AbstractServiceConfig { */ private Integer iothreads; + /** + * Thread pool keepAliveTime, default unit TimeUnit.MILLISECONDS + */ + private Integer alive; + /** * Thread pool queue length */ @@ -240,6 +245,14 @@ public void setIothreads(Integer iothreads) { this.iothreads = iothreads; } + public Integer getAlive() { + return alive; + } + + public void setAlive(Integer alive) { + this.alive = alive; + } + public Integer getQueues() { return queues; } @@ -425,4 +438,35 @@ public void setWait(Integer wait) { this.wait = wait; } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ProviderConfig{"); + sb.append("host='").append(host).append('\''); + sb.append(", port=").append(port); + sb.append(", contextpath='").append(contextpath).append('\''); + sb.append(", threadpool='").append(threadpool).append('\''); + sb.append(", threadname='").append(threadname).append('\''); + sb.append(", threads=").append(threads); + sb.append(", iothreads=").append(iothreads); + sb.append(", alive=").append(alive); + sb.append(", queues=").append(queues); + sb.append(", accepts=").append(accepts); + sb.append(", codec='").append(codec).append('\''); + sb.append(", charset='").append(charset).append('\''); + sb.append(", payload=").append(payload); + sb.append(", buffer=").append(buffer); + sb.append(", transporter='").append(transporter).append('\''); + sb.append(", exchanger='").append(exchanger).append('\''); + sb.append(", dispatcher='").append(dispatcher).append('\''); + sb.append(", networker='").append(networker).append('\''); + sb.append(", server='").append(server).append('\''); + sb.append(", client='").append(client).append('\''); + sb.append(", telnet='").append(telnet).append('\''); + sb.append(", prompt='").append(prompt).append('\''); + sb.append(", status='").append(status).append('\''); + sb.append(", wait=").append(wait); + sb.append(", isDefault=").append(isDefault); + sb.append('}'); + return sb.toString(); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java index 966b384c988..a16b49ebfab 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java @@ -42,11 +42,6 @@ public abstract class ReferenceConfigBase extends AbstractReferenceConfig { private static final long serialVersionUID = -5864351140409987595L; - /** - * The interface name of the reference service - */ - protected String interfaceName; - /** * The interface class of the reference service */ @@ -72,7 +67,6 @@ public abstract class ReferenceConfigBase extends AbstractReferenceConfig { */ protected String protocol; - protected ServiceMetadata serviceMetadata; public ReferenceConfigBase() { serviceMetadata = new ServiceMetadata(); @@ -160,18 +154,6 @@ public void setInterfaceClass(Class interfaceClass) { setInterface(interfaceClass); } - public String getInterface() { - return interfaceName; - } - - public void setInterface(String interfaceName) { - this.interfaceName = interfaceName; - // FIXME, add id strategy in ConfigManager -// if (StringUtils.isEmpty(id)) { -// id = interfaceName; -// } - } - public void setInterface(Class interfaceClass) { if (interfaceClass != null && !interfaceClass.isInterface()) { throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!"); @@ -259,12 +241,14 @@ public void resolveFile() { @Override protected void computeValidRegistryIds() { - super.computeValidRegistryIds(); - if (StringUtils.isEmpty(getRegistryIds())) { - if (getConsumer() != null && StringUtils.isNotEmpty(getConsumer().getRegistryIds())) { - setRegistryIds(getConsumer().getRegistryIds()); + if (consumer != null) { + if (notHasSelfRegistryProperty()) { + setRegistries(consumer.getRegistries()); + setRegistryIds(consumer.getRegistryIds()); } } + + super.computeValidRegistryIds(); } @Parameter(excluded = true) @@ -272,16 +256,6 @@ public String getUniqueServiceName() { return URL.buildKey(interfaceName, getGroup(), getVersion()); } - @Override - public String getVersion() { - return StringUtils.isEmpty(this.version) ? (consumer != null ? consumer.getVersion() : this.version) : this.version; - } - - @Override - public String getGroup() { - return StringUtils.isEmpty(this.group) ? (consumer != null ? consumer.getGroup() : this.group) : this.group; - } - public abstract T get(); public abstract void destroy(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java index f1b14d6df62..c213e49e6e2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java @@ -43,10 +43,7 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { private static final long serialVersionUID = 3033787999037024738L; - /** - * The interface name of the exported service - */ - protected String interfaceName; + /** * The interface class of the exported service @@ -78,7 +75,7 @@ public abstract class ServiceConfigBase extends AbstractServiceConfig { */ protected volatile String generic; - protected ServiceMetadata serviceMetadata; + public ServiceConfigBase() { serviceMetadata = new ServiceMetadata(); @@ -202,32 +199,35 @@ public void checkDefault() throws IllegalStateException { } public void checkProtocol() { + if (provider != null && notHasSelfProtocolProperty()) { + setProtocols(provider.getProtocols()); + setProtocolIds(provider.getProtocolIds()); + } + if (CollectionUtils.isEmpty(protocols) && provider != null) { setProtocols(provider.getProtocols()); } convertProtocolIdsToProtocols(); } + private boolean notHasSelfProtocolProperty() { + return CollectionUtils.isEmpty(protocols) && StringUtils.isEmpty(protocolIds); + } + public void completeCompoundConfigs() { super.completeCompoundConfigs(provider); if (provider != null) { - if (protocols == null) { + if (notHasSelfProtocolProperty()) { setProtocols(provider.getProtocols()); + setProtocolIds(provider.getProtocolIds()); } if (configCenter == null) { setConfigCenter(provider.getConfigCenter()); } - if (StringUtils.isEmpty(registryIds)) { - setRegistryIds(provider.getRegistryIds()); - } - if (StringUtils.isEmpty(protocolIds)) { - setProtocolIds(provider.getProtocolIds()); - } } } private void convertProtocolIdsToProtocols() { - computeValidProtocolIds(); if (StringUtils.isEmpty(protocolIds)) { if (CollectionUtils.isEmpty(protocols)) { List protocolConfigs = ApplicationModel.getConfigManager().getDefaultProtocols(); @@ -292,9 +292,7 @@ public void setInterfaceClass(Class interfaceClass) { setInterface(interfaceClass); } - public String getInterface() { - return interfaceName; - } + public void setInterface(Class interfaceClass) { if (interfaceClass != null && !interfaceClass.isInterface()) { @@ -304,14 +302,6 @@ public void setInterface(Class interfaceClass) { setInterface(interfaceClass == null ? null : interfaceClass.getName()); } - public void setInterface(String interfaceName) { - this.interfaceName = interfaceName; - // FIXME, add id strategy in ConfigManager -// if (StringUtils.isEmpty(id)) { -// id = interfaceName; -// } - } - public T getRef() { return ref; } @@ -403,32 +393,16 @@ public String getUniqueServiceName() { return URL.buildKey(interfaceName, getGroup(), getVersion()); } - @Override - public String getGroup() { - return StringUtils.isEmpty(this.group) ? (provider != null ? provider.getGroup() : this.group) : this.group; - } - - @Override - public String getVersion() { - return StringUtils.isEmpty(this.version) ? (provider != null ? provider.getVersion() : this.version) : this.version; - } - private void computeValidProtocolIds() { - if (StringUtils.isEmpty(getProtocolIds())) { - if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getProtocolIds())) { - setProtocolIds(getProvider().getProtocolIds()); - } - } - } @Override protected void computeValidRegistryIds() { - super.computeValidRegistryIds(); - if (StringUtils.isEmpty(getRegistryIds())) { - if (getProvider() != null && StringUtils.isNotEmpty(getProvider().getRegistryIds())) { - setRegistryIds(getProvider().getRegistryIds()); - } + if (provider != null && notHasSelfRegistryProperty()) { + setRegistries(provider.getRegistries()); + setRegistryIds(provider.getRegistryIds()); } + + super.computeValidRegistryIds(); } public abstract void export(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java index 3be7d3ef5c0..83f86594c71 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/SslConfig.java @@ -18,10 +18,10 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.IOUtils; import org.apache.dubbo.config.support.Parameter; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicBoolean; @@ -120,9 +120,9 @@ public void setClientTrustCertCollectionPath(String clientTrustCertCollectionPat this.clientTrustCertCollectionPath = clientTrustCertCollectionPath; } - public InputStream getServerKeyCertChainPathStream() throws FileNotFoundException { + public InputStream getServerKeyCertChainPathStream() throws IOException { if (serverKeyCertChainPath != null) { - serverKeyCertChainPathStream = new FileInputStream(serverKeyCertChainPath); + serverKeyCertChainPathStream = IOUtils.getURL(serverKeyCertChainPath).openStream(); } return serverKeyCertChainPathStream; } @@ -131,9 +131,9 @@ public void setServerKeyCertChainPathStream(InputStream serverKeyCertChainPathSt this.serverKeyCertChainPathStream = serverKeyCertChainPathStream; } - public InputStream getServerPrivateKeyPathStream() throws FileNotFoundException { + public InputStream getServerPrivateKeyPathStream() throws IOException { if (serverPrivateKeyPath != null) { - serverPrivateKeyPathStream = new FileInputStream(serverPrivateKeyPath); + serverPrivateKeyPathStream = IOUtils.getURL(serverPrivateKeyPath).openStream(); } return serverPrivateKeyPathStream; } @@ -142,9 +142,9 @@ public void setServerPrivateKeyPathStream(InputStream serverPrivateKeyPathStream this.serverPrivateKeyPathStream = serverPrivateKeyPathStream; } - public InputStream getServerTrustCertCollectionPathStream() throws FileNotFoundException { + public InputStream getServerTrustCertCollectionPathStream() throws IOException { if (serverTrustCertCollectionPath != null) { - serverTrustCertCollectionPathStream = new FileInputStream(serverTrustCertCollectionPath); + serverTrustCertCollectionPathStream = IOUtils.getURL(serverTrustCertCollectionPath).openStream(); } return serverTrustCertCollectionPathStream; } @@ -153,9 +153,9 @@ public void setServerTrustCertCollectionPathStream(InputStream serverTrustCertCo this.serverTrustCertCollectionPathStream = serverTrustCertCollectionPathStream; } - public InputStream getClientKeyCertChainPathStream() throws FileNotFoundException { + public InputStream getClientKeyCertChainPathStream() throws IOException { if (clientKeyCertChainPath != null) { - clientKeyCertChainPathStream = new FileInputStream(clientKeyCertChainPath); + clientKeyCertChainPathStream = IOUtils.getURL(clientKeyCertChainPath).openStream(); } return clientKeyCertChainPathStream; } @@ -164,9 +164,9 @@ public void setClientKeyCertChainPathStream(InputStream clientKeyCertChainPathSt this.clientKeyCertChainPathStream = clientKeyCertChainPathStream; } - public InputStream getClientPrivateKeyPathStream() throws FileNotFoundException { + public InputStream getClientPrivateKeyPathStream() throws IOException { if (clientPrivateKeyPath != null) { - clientPrivateKeyPathStream = new FileInputStream(clientPrivateKeyPath); + clientPrivateKeyPathStream = IOUtils.getURL(clientPrivateKeyPath).openStream(); } return clientPrivateKeyPathStream; } @@ -175,9 +175,9 @@ public void setClientPrivateKeyPathStream(InputStream clientPrivateKeyPathStream this.clientPrivateKeyPathStream = clientPrivateKeyPathStream; } - public InputStream getClientTrustCertCollectionPathStream() throws FileNotFoundException { + public InputStream getClientTrustCertCollectionPathStream() throws IOException { if (clientTrustCertCollectionPath != null) { - clientTrustCertCollectionPathStream = new FileInputStream(clientTrustCertCollectionPath); + clientTrustCertCollectionPathStream = IOUtils.getURL(clientTrustCertCollectionPath).openStream(); } return clientTrustCertCollectionPathStream; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java index 5492ac08d90..bddf4d84392 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java @@ -285,7 +285,14 @@ /** * @return The service names that the Dubbo interface subscribed * @see RegistryConstants#SUBSCRIBED_SERVICE_NAMES_KEY + * @deprecated using {@link DubboReference#providedBy()} * @since 2.7.8 */ String[] services() default {}; + + /** + * declares which app or service this interface belongs to + * @see RegistryConstants#PROVIDED_BY + */ + String[] providedBy() default {}; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java index dca7978b2e7..3251f63f3b3 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigConfigurationAdapter.java @@ -32,7 +32,7 @@ public class ConfigConfigurationAdapter implements Configuration { public ConfigConfigurationAdapter(AbstractConfig config) { Map configMetadata = config.getMetaData(); - metaData = new HashMap<>(configMetadata.size()); + metaData = new HashMap<>(configMetadata.size(), 1.0f); for (Map.Entry entry : configMetadata.entrySet()) { String prefix = config.getPrefix().endsWith(".") ? config.getPrefix() : config.getPrefix() + "."; String id = StringUtils.isEmpty(config.getId()) ? "" : config.getId() + "."; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java index a64020413ae..8fd7a521418 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.BaseServiceMetadata; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.config.ServiceConfigBase; import java.lang.reflect.Method; @@ -184,7 +185,7 @@ private void initMethod(Class serviceInterfaceClass) { methodsToExport = serviceInterfaceClass.getMethods(); for (Method method : methodsToExport) { - method.setAccessible(true); + ReflectUtils.makeAccessible(method); List methodModels = methods.get(method.getName()); if (methodModels == null) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceDescriptor.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceDescriptor.java index 1c12672761b..445142c2999 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceDescriptor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceDescriptor.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.model; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import java.lang.reflect.Method; import java.util.Arrays; @@ -47,7 +48,7 @@ public ServiceDescriptor(Class interfaceClass) { private void initMethods() { Method[] methodsToExport = this.serviceInterfaceClass.getMethods(); for (Method method : methodsToExport) { - method.setAccessible(true); + ReflectUtils.makeAccessible(method); List methodModels = methods.computeIfAbsent(method.getName(), (k) -> new ArrayList<>(1)); methodModels.add(new MethodDescriptor(method)); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/GroupServiceKeyCache.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/GroupServiceKeyCache.java index 3b37b59ccbd..f040f3c9480 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/GroupServiceKeyCache.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/GroupServiceKeyCache.java @@ -62,7 +62,7 @@ private String createServiceKey(String serviceName, String serviceVersion, int p } buf.append(serviceName); - if (StringUtils.isNotEmpty(serviceVersion) && !"0.0.0".equals(serviceVersion)) { + if (StringUtils.isNotEmpty(serviceVersion) && !"0.0.0".equals(serviceVersion) && !"*".equals(serviceVersion)) { buf.append(':').append(serviceVersion); } buf.append(':').append(port); diff --git a/dubbo-common/src/main/resources/security/serialize.blockedlist b/dubbo-common/src/main/resources/security/serialize.blockedlist new file mode 100644 index 00000000000..de0b68de639 --- /dev/null +++ b/dubbo-common/src/main/resources/security/serialize.blockedlist @@ -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. +# +# +aj.org.objectweb.asm. +br.com.anteros. +ch.qos.logback. +clojure.core$constantly +clojure.main$eval_opt +com.alibaba.citrus.springext.support.parser.abstractnamedproxybeandefinitionparser$proxytargetfactory +com.alibaba.citrus.springext.util.springextutil.abstractproxy +com.alibaba.druid.pool.druiddatasource +com.alibaba.druid.stat.jdbcdatasourcestat +com.alibaba.fastjson.annotation +com.alipay.custrelation.service.model.redress.pair +com.caucho. +com.ibatis. +com.mchange +com.mysql.cj.jdbc.admin. +com.mysql.cj.jdbc.mysqlconnectionpooldatasource +com.mysql.cj.jdbc.mysqldatasource +com.mysql.cj.jdbc.mysqlxadatasource +com.mysql.cj.log. +com.p6spy.engine. +com.rometools.rome.feed.impl.equalsbean +com.rometools.rome.feed.impl.tostringbean +com.sun. +com.taobao.eagleeye.wrapper +com.zaxxer.hikari. +flex.messaging.util.concurrent. +java.awt.i +java.awt.p +java.beans.expression +java.io.closeable +java.io.serializable +java.lang.autocloseable +java.lang.class +java.lang.cloneable +java.lang.iterable +java.lang.object +java.lang.readable +java.lang.runnable +java.lang.thread +java.lang.unixprocess +java.net.inetaddress +java.net.socket +java.net.url +java.rmi +java.security.signedobject +java.util.collection +java.util.eventlistener +java.util.jar. +java.util.logging. +java.util.prefs. +java.util.serviceloader$lazyiterator +javassist. +javax.activation. +javax.imageio.imageio$containsfilter +javax.imageio.spi.serviceregistry +javax.management. +javax.naming. +javax.net. +javax.print. +javax.script. +javax.sound. +javax.swing.j +javax.tools. +javax.xml +jdk.internal. +jodd.db.connection. +junit. +net.bytebuddy.dynamic.loading.bytearrayclassloader +net.sf.cglib. +net.sf.ehcache.hibernate. +net.sf.ehcache.transaction.manager. +oracle.jdbc. +oracle.jms.aq +oracle.net +org.aoju.bus.proxy.provider. +org.apache.activemq.activemqconnectionfactory +org.apache.activemq.activemqxaconnectionfactory +org.apache.activemq.jms.pool. +org.apache.activemq.pool. +org.apache.activemq.spring. +org.apache.aries.transaction. +org.apache.axis2.jaxws.spi.handler. +org.apache.axis2.transport.jms. +org.apache.bcel +org.apache.carbondata.core.scan.expression.expressionresult +org.apache.catalina. +org.apache.cocoon. +org.apache.commons.beanutils +org.apache.commons.collections.comparators. +org.apache.commons.collections.functors +org.apache.commons.collections.functors. +org.apache.commons.collections.transformer +org.apache.commons.collections4.comparators +org.apache.commons.collections4.functors +org.apache.commons.collections4.transformer +org.apache.commons.configuration +org.apache.commons.dbcp +org.apache.commons.fileupload +org.apache.commons.jelly. +org.apache.commons.logging. +org.apache.commons.proxy. +org.apache.cxf.jaxrs.provider. +org.apache.hadoop.shaded.com.zaxxer.hikari. +org.apache.http.auth. +org.apache.http.conn. +org.apache.http.cookie. +org.apache.http.impl. +org.apache.ibatis.datasource +org.apache.ibatis.executor. +org.apache.ibatis.javassist. +org.apache.ibatis.ognl. +org.apache.ibatis.parsing. +org.apache.ibatis.reflection. +org.apache.ibatis.scripting. +org.apache.ignite.cache.jta. +org.apache.log4j. +org.apache.logging. +org.apache.myfaces.context.servlet +org.apache.openjpa.ee. +org.apache.shiro.jndi. +org.apache.shiro.realm. +org.apache.tomcat +org.apache.wicket.util +org.apache.xalan +org.apache.xbean. +org.apache.xpath.xpathcontext +org.codehaus.groovy.runtime +org.codehaus.jackson. +org.eclipse.jetty. +org.geotools.filter.constantexpression +org.h2.jdbcx. +org.h2.server. +org.hibernate +org.javasimon. +org.jaxen. +org.jboss +org.jdom. +org.jdom2.transform. +org.logicalcobwebs. +org.mortbay.jetty. +org.mozilla.javascript +org.objectweb.asm. +org.osjava.sj. +org.python.core +org.quartz. +org.slf4j. +org.springframework. +org.yaml.snakeyaml.tokens.directivetoken +sun.rmi.server.unicastref \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.java new file mode 100644 index 00000000000..1075aec70d8 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/BaseServiceMetadataTest.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.dubbo.common; + +import org.junit.jupiter.api.Test; + +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_VERSION; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class BaseServiceMetadataTest { + + @Test + public void test() { + BaseServiceMetadata baseServiceMetadata = new BaseServiceMetadata(); + baseServiceMetadata.setGroup("group1"); + baseServiceMetadata.setServiceInterfaceName("org.apache.dubbo.common.TestInterface"); + baseServiceMetadata.setVersion("1.0.0"); + baseServiceMetadata.setServiceKey(BaseServiceMetadata.buildServiceKey("org.apache.dubbo.common.TestInterface", "group1", "1.0.0")); + + assertEquals(baseServiceMetadata.getGroup(), "group1"); + assertEquals(baseServiceMetadata.getServiceInterfaceName(), "org.apache.dubbo.common.TestInterface"); + assertEquals(baseServiceMetadata.getVersion(), "1.0.0"); + assertEquals(baseServiceMetadata.getServiceKey(), "group1/org.apache.dubbo.common.TestInterface:1.0.0"); + assertEquals(baseServiceMetadata.getDisplayServiceKey(), "org.apache.dubbo.common.TestInterface:1.0.0"); + + baseServiceMetadata.setServiceKey(BaseServiceMetadata.buildServiceKey("org.apache.dubbo.common.TestInterface", null, null)); + assertEquals(baseServiceMetadata.getServiceKey(), "org.apache.dubbo.common.TestInterface"); + baseServiceMetadata.setServiceKey(BaseServiceMetadata.buildServiceKey("org.apache.dubbo.common.TestInterface", "", "")); + assertEquals(baseServiceMetadata.getServiceKey(), "org.apache.dubbo.common.TestInterface"); + + + baseServiceMetadata.setVersion("2.0.0"); + baseServiceMetadata.generateServiceKey(); + assertEquals(baseServiceMetadata.getServiceKey(), "group1/org.apache.dubbo.common.TestInterface:2.0.0"); + + assertEquals(BaseServiceMetadata.versionFromServiceKey("group1/org.apache.dubbo.common.TestInterface:1.0.0"), "1.0.0"); + assertEquals(BaseServiceMetadata.groupFromServiceKey("group1/org.apache.dubbo.common.TestInterface:1.0.0"), "group1"); + assertEquals(BaseServiceMetadata.interfaceFromServiceKey("group1/org.apache.dubbo.common.TestInterface:1.0.0"), "org.apache.dubbo.common.TestInterface"); + + assertEquals(DEFAULT_VERSION, BaseServiceMetadata.versionFromServiceKey("")); + assertNull(BaseServiceMetadata.groupFromServiceKey("")); + assertEquals(BaseServiceMetadata.interfaceFromServiceKey(""), ""); + + assertEquals(BaseServiceMetadata.revertDisplayServiceKey("org.apache.dubbo.common.TestInterface:1.0.0").getDisplayServiceKey(), + "org.apache.dubbo.common.TestInterface:1.0.0"); + assertEquals(BaseServiceMetadata.revertDisplayServiceKey("org.apache.dubbo.common.TestInterface").getDisplayServiceKey(), + "org.apache.dubbo.common.TestInterface:null"); + assertEquals(BaseServiceMetadata.revertDisplayServiceKey(null).getDisplayServiceKey(),"null:null"); + assertEquals(BaseServiceMetadata.revertDisplayServiceKey("org.apache.dubbo.common.TestInterface:1.0.0:1").getDisplayServiceKey(),"null:null"); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java new file mode 100644 index 00000000000..5fe175befc8 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/PojoUtilsForNonPublicStaticTest.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common; + +import org.apache.dubbo.common.utils.PojoUtils; +import org.junit.jupiter.api.Test; + +public class PojoUtilsForNonPublicStaticTest { + + @Test + public void testNonPublicStaticClass() { + NonPublicStaticData nonPublicStaticData = new NonPublicStaticData("horizon"); + PojoUtils.generalize(nonPublicStaticData); + } + + /** + * the static class need is not same package with PojoUtils, so define it here. + */ + static class NonPublicStaticData { + + private String name; + + public NonPublicStaticData(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java index 3ca62ce83c4..f295de9f57d 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java @@ -16,8 +16,13 @@ */ package org.apache.dubbo.common; +import net.bytebuddy.utility.RandomString; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.HashSet; +import java.util.Set; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -25,16 +30,53 @@ * Created by LinShunkang on 2020/03/12 */ public class URLStrParserTest { + private static Set testCases = new HashSet<>(16); + private static Set errorDecodedCases = new HashSet<>(8); + private static Set errorEncodedCases = new HashSet<>(8); + + static { + testCases.add("dubbo://192.168.1.1"); + testCases.add("dubbo://192.168.1.1?"); + testCases.add("dubbo://127.0.0.1?test=中文测试"); + testCases.add("dubbo://admin:admin123@192.168.1.41:28113/org.test.api.DemoService$Iface?anyhost=true&application=demo-service&dubbo=2.6.1&generic=false&interface=org.test.api.DemoService$Iface&methods=orbCompare,checkText,checkPicture&pid=65557&revision=1.4.17&service.filter=bootMetrics&side=provider&status=server&threads=200×tamp=1583136298859&version=1.0.0"); + // super long text test + testCases.add("dubbo://192.168.1.1/" + RandomString.make(10240)); + testCases.add("file:/path/to/file.txt"); + testCases.add("dubbo://fe80:0:0:0:894:aeec:f37d:23e1%en0/path?abc=abc"); + + errorDecodedCases.add("dubbo:192.168.1.1"); + errorDecodedCases.add("://192.168.1.1"); + errorDecodedCases.add(":/192.168.1.1"); + + errorEncodedCases.add("dubbo%3a%2f%2f192.168.1.41%3fabc%3"); + errorEncodedCases.add("dubbo%3a192.168.1.1%3fabc%3dabc"); + errorEncodedCases.add("%3a%2f%2f192.168.1.1%3fabc%3dabc"); + errorEncodedCases.add("%3a%2f192.168.1.1%3fabc%3dabc"); + errorEncodedCases.add("dubbo%3a%2f%2f127.0.0.1%3ftest%3d%e2%96%b2%e2%96%bc%e2%97%80%e2%96%b6%e2%86%90%e2%86%91%e2%86%92%e2%86%93%e2%86%94%e2%86%95%e2%88%9e%c2%b1%e9%be%98%e9%9d%90%e9%bd%89%9%d%b"); + } @Test - public void test() { - String str = "dubbo%3A%2F%2Fadmin%3Aadmin123%40192.168.1.41%3A28113%2Forg.test.api.DemoService%24Iface%3Fanyhost%3Dtrue%26application%3Ddemo-service%26dubbo%3D2.6.1%26generic%3Dfalse%26interface%3Dorg.test.api.DemoService%24Iface%26methods%3DorbCompare%2CcheckText%2CcheckPicture%26pid%3D65557%26revision%3D1.4.17%26service.filter%3DbootMetrics%26side%3Dprovider%26status%3Dserver%26threads%3D200%26timestamp%3D1583136298859%26version%3D1.0.0"; - System.out.println(URLStrParser.parseEncodedStr(str)); - - String decodeStr = URL.decode(str); - URL originalUrl = URL.valueOf(decodeStr); - assertThat(URLStrParser.parseEncodedStr(str), equalTo(originalUrl)); - assertThat(URLStrParser.parseDecodedStr(decodeStr), equalTo(originalUrl)); + public void testEncoded() { + testCases.forEach(testCase -> { + assertThat(URLStrParser.parseEncodedStr(URL.encode(testCase)), equalTo(URL.valueOf(testCase))); + }); + + errorEncodedCases.forEach(errorCase -> { + Assertions.assertThrows(RuntimeException.class, + () -> URLStrParser.parseEncodedStr(errorCase)); + }); + } + + @Test + public void testDecoded() { + testCases.forEach(testCase -> { + assertThat(URLStrParser.parseDecodedStr(testCase), equalTo(URL.valueOf(testCase))); + }); + + errorDecodedCases.forEach(errorCase -> { + Assertions.assertThrows(RuntimeException.class, + () -> URLStrParser.parseDecodedStr(errorCase)); + }); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java index 5ba606d5ad7..86ebb919dd7 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java @@ -31,9 +31,11 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.fail; public class URLTest { @@ -897,4 +899,106 @@ public void testGetParameter() { assertEquals(Integer.valueOf(1), url.getParameter("i", Integer.class)); assertEquals(Boolean.FALSE, url.getParameter("b", Boolean.class)); } + + @Test + public void testEquals() { + URL url1 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=1599556506417"); + URL url2 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertEquals(url1, url2); + + URL url3 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url2, url3); + + URL url4 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=true&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url3, url4); + } + + @Test + public void testHashcode() { + URL url1 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=1599556506417"); + URL url2 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertEquals(url1.hashCode(), url2.hashCode()); + + URL url3 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=false&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url2.hashCode(), url3.hashCode()); + + URL url4 = URL.valueOf("consumer://30.225.20.150/org.apache.dubbo.rpc.service.GenericService?application=" + + "dubbo-demo-api-consumer&category=consumers&check=true&dubbo=2.0.2&interface=" + + "org.apache.dubbo.demo.DemoService&pid=7375&side=consumer&sticky=false×tamp=2299556506417"); + assertNotEquals(url3.hashCode(), url4.hashCode()); + } + + @Test + public void testEqualsWithPassword() { + URL url1 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min:hello@4321@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithPath() { + URL url1 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path1?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path2?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path1?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithPort() { + URL url1 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20881/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithProtocol() { + URL url1 = URL.valueOf("dubbo://ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("file://ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("dubbo://ad@min:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } + + @Test + public void testEqualsWithUser() { + URL url1 = URL.valueOf("ad@min1:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url2 = URL.valueOf("ad@min2:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + URL url3 = URL.valueOf("ad@min1:hello@1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"); + + boolean actual1 = url1.equals(url2); + boolean actual2 = url1.equals(url3); + assertFalse(actual1); + assertTrue(actual2); + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/ClassGeneratorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/ClassGeneratorTest.java index c49554623ec..1db84905e59 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/ClassGeneratorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/ClassGeneratorTest.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.common.bytecode; +import org.apache.dubbo.common.utils.ReflectUtils; + import org.junit.jupiter.api.Test; import java.lang.reflect.Field; @@ -35,7 +37,7 @@ public void testMain() throws Exception { Bean b = new Bean(); Field fname = null, fs[] = Bean.class.getDeclaredFields(); for (Field f : fs) { - f.setAccessible(true); + ReflectUtils.makeAccessible(f); if (f.getName().equals("name")) fname = f; } @@ -65,7 +67,7 @@ public void testMain0() throws Exception { Bean b = new Bean(); Field fname = null, fs[] = Bean.class.getDeclaredFields(); for (Field f : fs) { - f.setAccessible(true); + ReflectUtils.makeAccessible(f); if (f.getName().equals("name")) fname = f; } @@ -105,4 +107,4 @@ public String getName() { } public static volatile String abc = "df"; -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java index d3c4d906a08..cc246f8ae97 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/bytecode/WrapperTest.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; public class WrapperTest { @@ -113,6 +114,26 @@ public void testNoSuchMethod() throws Exception { }); } + @Test + public void testOverloadMethod() throws Exception { + Wrapper w = Wrapper.getWrapper(I2.class); + assertEquals(2, w.getMethodNames().length); + + Impl2 impl = new Impl2(); + + w.invokeMethod(impl, "setFloat", new Class[]{float.class}, new Object[]{1F}); + assertEquals(1F, impl.getFloat1()); + assertNull(impl.getFloat2()); + + w.invokeMethod(impl, "setFloat", new Class[]{Float.class}, new Object[]{2f}); + assertEquals(1F, impl.getFloat1()); + assertEquals(2F, impl.getFloat2()); + + w.invokeMethod(impl, "setFloat", new Class[]{Float.class}, new Object[]{null}); + assertEquals(1F, impl.getFloat1()); + assertNull(impl.getFloat2()); + } + @Test public void test_getDeclaredMethodNames_ContainExtendsParentMethods() throws Exception { assertArrayEquals(new String[]{"hello",}, Wrapper.getWrapper(Parent1.class).getMethodNames()); @@ -141,6 +162,12 @@ public interface I1 extends I0 { void setFloat(float f); } + public interface I2 { + void setFloat(float f); + + void setFloat(Float f); + } + public interface EmptyService { } @@ -191,6 +218,29 @@ public void setFloat(float f) { } } + public static class Impl2 implements I2 { + private float float1; + private Float float2; + + @Override + public void setFloat(float f) { + this.float1 = f; + } + + @Override + public void setFloat(Float f) { + this.float2 = f; + } + + public float getFloat1() { + return float1; + } + + public Float getFloat2() { + return float2; + } + } + public static class EmptyServiceImpl implements EmptyService { } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentConfigurationTest.java index 974e07c8a78..2987fcd482a 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentConfigurationTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/EnvironmentConfigurationTest.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.common.config; +import org.apache.dubbo.common.utils.ReflectUtils; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -64,11 +66,11 @@ protected static void setEnv(Map newenv) throws Exception { try { Class processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment"); - theEnvironmentField.setAccessible(true); + ReflectUtils.makeAccessible(theEnvironmentField); Map env = (Map) theEnvironmentField.get(null); env.putAll(newenv); Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment"); - theCaseInsensitiveEnvironmentField.setAccessible(true); + ReflectUtils.makeAccessible(theCaseInsensitiveEnvironmentField); Map cienv = (Map) theCaseInsensitiveEnvironmentField.get(null); cienv.putAll(newenv); } catch (NoSuchFieldException e) { @@ -77,7 +79,7 @@ protected static void setEnv(Map newenv) throws Exception { for (Class cl : classes) { if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { Field field = cl.getDeclaredField("m"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); Object obj = field.get(env); Map map = (Map) obj; map.clear(); @@ -90,7 +92,7 @@ protected static void setEnv(Map newenv) throws Exception { private static void updateEnv(String name, String val) throws ReflectiveOperationException { Map env = System.getenv(); Field field = env.getClass().getDeclaredField("m"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); ((Map) field.get(env)).put(name, val); } /** @@ -101,4 +103,4 @@ public void clean(){ } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java index 91549275d44..7725399ab2b 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java @@ -28,28 +28,28 @@ */ public class ConfigChangedEventTest { - private static final String key = "k"; + private static final String KEY = "k"; - private static final String group = "g"; + private static final String GROUP = "g"; - private static final String content = "c"; + private static final String CONTENT = "c"; @Test public void testGetter() { - ConfigChangedEvent event = new ConfigChangedEvent(key, group, content); + ConfigChangedEvent event = new ConfigChangedEvent(KEY, GROUP, CONTENT); - assertEquals(key, event.getKey()); - assertEquals(group, event.getGroup()); - assertEquals(content, event.getContent()); + assertEquals(KEY, event.getKey()); + assertEquals(GROUP, event.getGroup()); + assertEquals(CONTENT, event.getContent()); assertEquals(ConfigChangeType.MODIFIED, event.getChangeType()); assertEquals("k,g", event.getSource()); - event = new ConfigChangedEvent(key, group, content, ConfigChangeType.ADDED); + event = new ConfigChangedEvent(KEY, GROUP, CONTENT, ConfigChangeType.ADDED); - assertEquals(key, event.getKey()); - assertEquals(group, event.getGroup()); - assertEquals(content, event.getContent()); + assertEquals(KEY, event.getKey()); + assertEquals(GROUP, event.getGroup()); + assertEquals(CONTENT, event.getContent()); assertEquals(ConfigChangeType.ADDED, event.getChangeType()); assertEquals("k,g", event.getSource()); } @@ -57,15 +57,15 @@ public void testGetter() { @Test public void testEqualsAndHashCode() { for (ConfigChangeType type : ConfigChangeType.values()) { - assertEquals(new ConfigChangedEvent(key, group, content, type), new ConfigChangedEvent(key, group, content, type)); - assertEquals(new ConfigChangedEvent(key, group, content, type).hashCode(), new ConfigChangedEvent(key, group, content, type).hashCode()); - assertEquals(new ConfigChangedEvent(key, group, content, type).toString(), new ConfigChangedEvent(key, group, content, type).toString()); + assertEquals(new ConfigChangedEvent(KEY, GROUP, CONTENT, type), new ConfigChangedEvent(KEY, GROUP, CONTENT, type)); + assertEquals(new ConfigChangedEvent(KEY, GROUP, CONTENT, type).hashCode(), new ConfigChangedEvent(KEY, GROUP, CONTENT, type).hashCode()); + assertEquals(new ConfigChangedEvent(KEY, GROUP, CONTENT, type).toString(), new ConfigChangedEvent(KEY, GROUP, CONTENT, type).toString()); } } @Test public void testToString() { - ConfigChangedEvent event = new ConfigChangedEvent(key, group, content); + ConfigChangedEvent event = new ConfigChangedEvent(KEY, GROUP, CONTENT); assertNotNull(event.toString()); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java index 80282c185c3..b35ebd11b1b 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java @@ -23,6 +23,7 @@ import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.File; @@ -43,6 +44,9 @@ /** * {@link FileSystemDynamicConfiguration} Test */ +// Test often failed on Github Actions Platform because of file system on Azure +//@DisabledIfEnvironmentVariable(named = "DISABLE_FILE_SYSTEM_TEST", matches = "true") +@Disabled public class FileSystemDynamicConfigurationTest { private final Logger logger = LoggerFactory.getLogger(getClass()); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java index a1a534a6d55..5d01d9da8c0 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.convert.StringToDoubleConverter; import org.apache.dubbo.common.convert.StringToIntegerConverter; import org.apache.dubbo.common.extension.activate.ActivateExt1; +import org.apache.dubbo.common.extension.activate.ActivateWrapperExt1; import org.apache.dubbo.common.extension.activate.impl.ActivateExt1Impl1; import org.apache.dubbo.common.extension.activate.impl.GroupActivateExtImpl; import org.apache.dubbo.common.extension.activate.impl.OldActivateExt1Impl2; @@ -69,6 +70,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; import static org.apache.dubbo.common.extension.ExtensionLoader.getLoadingStrategies; +import static org.apache.dubbo.common.extension.ExtensionLoader.resetExtensionLoader; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.instanceOf; @@ -158,6 +160,14 @@ public void test_getExtension_WithWrapper() throws Exception { assertEquals(echoCount2 + 1, Ext5Wrapper2.echoCount.get()); } + @Test + public void test_getActivateExtension_WithWrapper() throws Exception { + URL url = URL.valueOf("test://localhost/test"); + List list = getExtensionLoader(ActivateWrapperExt1.class) + .getActivateExtension(url, new String[]{}, "order"); + assertEquals(2, list.size()); + } + @Test public void test_getExtension_ExceptionNoExtension() throws Exception { try { @@ -255,6 +265,7 @@ public void test_AddExtension() throws Exception { assertThat(ext, instanceOf(AddExt1_ManualAdd1.class)); assertEquals("Manual1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd1.class)); + ExtensionLoader.resetExtensionLoader(AddExt1.class); } @Test @@ -324,6 +335,7 @@ public void test_replaceExtension() throws Exception { assertThat(ext, instanceOf(AddExt1_ManualAdd2.class)); assertEquals("impl1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd2.class)); } + ExtensionLoader.resetExtensionLoader(AddExt1.class); } @Test @@ -337,6 +349,7 @@ public void test_replaceExtension_Adaptive() throws Exception { adaptive = loader.getAdaptiveExtension(); assertTrue(adaptive instanceof AddExt3_ManualAdaptive); + ExtensionLoader.resetExtensionLoader(AddExt3.class); } @Test @@ -374,7 +387,7 @@ public void test_InitError() throws Exception { fail(); } catch (IllegalStateException expected) { assertThat(expected.getMessage(), containsString("Failed to load extension class (interface: interface org.apache.dubbo.common.extension.ext7.InitErrorExt")); - assertThat(expected.getCause(), instanceOf(ExceptionInInitializerError.class)); + assertThat(expected.getMessage(), containsString("java.lang.ExceptionInInitializerError")); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/ActivateWrapperExt1.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/ActivateWrapperExt1.java new file mode 100644 index 00000000000..1ae23227c7a --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/ActivateWrapperExt1.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.common.extension.activate; + +import org.apache.dubbo.common.extension.SPI; + +@SPI("extImp1") +public interface ActivateWrapperExt1 { + String echo(String msg); +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl1.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl1.java new file mode 100644 index 00000000000..0ff2ef44f4d --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl1.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.dubbo.common.extension.activate.impl; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.extension.activate.ActivateWrapperExt1; + +@Activate(order = 1, group = {"order"}) +public class ActivateWrapperExt1Impl1 implements ActivateWrapperExt1 { + public String echo(String msg) { + return msg; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl2.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl2.java new file mode 100644 index 00000000000..716d8150403 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Impl2.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.dubbo.common.extension.activate.impl; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.extension.activate.ActivateWrapperExt1; + +@Activate(order = 2, group = {"order"}) +public class ActivateWrapperExt1Impl2 implements ActivateWrapperExt1 { + public String echo(String msg) { + return msg; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Wrapper.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Wrapper.java new file mode 100644 index 00000000000..671a3b9c005 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/activate/impl/ActivateWrapperExt1Wrapper.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.common.extension.activate.impl; + +import org.apache.dubbo.common.extension.activate.ActivateWrapperExt1; + + +public class ActivateWrapperExt1Wrapper implements ActivateWrapperExt1 { + private ActivateWrapperExt1 activateWrapperExt1; + + public ActivateWrapperExt1Wrapper(ActivateWrapperExt1 activateWrapperExt1) { + this.activateWrapperExt1 = activateWrapperExt1; + } + + @Override + public String echo(String msg) { + return activateWrapperExt1.echo(msg); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/ActivateComparatorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/ActivateComparatorTest.java index 8b9a1d6399f..e3f41598131 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/ActivateComparatorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/support/ActivateComparatorTest.java @@ -32,19 +32,19 @@ public void testActivateComparator(){ Filter3 f3 = new Filter3(); Filter4 f4 = new Filter4(); OldFilter5 f5 = new OldFilter5(); - List filters = new ArrayList<>(); - filters.add(f1); - filters.add(f2); - filters.add(f3); - filters.add(f4); - filters.add(f5); + List filters = new ArrayList<>(); + filters.add(f1.getClass()); + filters.add(f2.getClass()); + filters.add(f3.getClass()); + filters.add(f4.getClass()); + filters.add(f5.getClass()); Collections.sort(filters, ActivateComparator.COMPARATOR); - Assertions.assertEquals(f4, filters.get(0)); - Assertions.assertEquals(f5, filters.get(1)); - Assertions.assertEquals(f3, filters.get(2)); - Assertions.assertEquals(f2, filters.get(3)); - Assertions.assertEquals(f1, filters.get(4)); + Assertions.assertEquals(f4.getClass(), filters.get(0)); + Assertions.assertEquals(f5.getClass(), filters.get(1)); + Assertions.assertEquals(f3.getClass(), filters.get(2)); + Assertions.assertEquals(f2.getClass(), filters.get(3)); + Assertions.assertEquals(f1.getClass(), filters.get(4)); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.java new file mode 100644 index 00000000000..b51e3e1df45 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/Demo.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.dubbo.common.extension.wrapper; + +import org.apache.dubbo.common.extension.SPI; + +@SPI("demo") +public interface Demo { + String echo(String msg); +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.java new file mode 100644 index 00000000000..cf6f5079d0a --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/WrapperTest.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.dubbo.common.extension.wrapper; + +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper; +import org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper2; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link org.apache.dubbo.common.extension.Wrapper} Test + * + * @since 2.7.5 + */ +public class WrapperTest { + + @Test + public void testWrapper() { + Demo demoWrapper = ExtensionLoader.getExtensionLoader(Demo.class).getExtension("demo"); + assertTrue(demoWrapper instanceof DemoWrapper); + Demo demoWrapper2 = ExtensionLoader.getExtensionLoader(Demo.class).getExtension("demo2"); + assertTrue(demoWrapper2 instanceof DemoWrapper2); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java new file mode 100644 index 00000000000..abcd5e687c0 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoImpl.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.extension.wrapper.impl; + +import org.apache.dubbo.common.extension.wrapper.Demo; + +public class DemoImpl implements Demo { + @Override + public String echo(String msg) { + return msg; + } +} diff --git a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/utils/ReflectionUtils.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.java similarity index 64% rename from dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/utils/ReflectionUtils.java rename to dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.java index 7c3b45a816d..44ec737a1fb 100644 --- a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/utils/ReflectionUtils.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper.java @@ -14,20 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.common.serialize.kryo.utils; +package org.apache.dubbo.common.extension.wrapper.impl; -public abstract class ReflectionUtils { +import org.apache.dubbo.common.extension.Wrapper; +import org.apache.dubbo.common.extension.wrapper.Demo; - public static boolean checkZeroArgConstructor(Class clazz) { - try { - clazz.getDeclaredConstructor(); - return true; - } catch (NoSuchMethodException e) { - return false; - } +@Wrapper(matches = {"demo"}, mismatches = "demo2") +public class DemoWrapper implements Demo { + private Demo demo; + + public DemoWrapper(Demo demo) { + this.demo = demo; } - public static boolean isJdk(Class clazz) { - return clazz.getName().startsWith("java.") || clazz.getName().startsWith("javax."); + public String echo(String msg) { + return demo.echo(msg); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.java new file mode 100644 index 00000000000..c8e4385474e --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/wrapper/impl/DemoWrapper2.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.dubbo.common.extension.wrapper.impl; + +import org.apache.dubbo.common.extension.Wrapper; +import org.apache.dubbo.common.extension.wrapper.Demo; + +@Wrapper(matches = {"demo2"}, mismatches = {"demo"}) +public class DemoWrapper2 implements Demo { + private Demo demo; + + public DemoWrapper2(Demo demo) { + this.demo = demo; + } + + public String echo(String msg) { + return demo.echo(msg); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java b/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java index d78c5928ea8..730d2cb3193 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/model/SerializablePerson.java @@ -27,6 +27,9 @@ public class SerializablePerson implements Serializable { private String[] value = {"value1", "value2"}; + public SerializablePerson(char description , boolean adult){ + + } public String getName() { return name; } @@ -94,4 +97,4 @@ public boolean equals(Object obj) { return false; return true; } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java index df0b908a6c6..8df34f8e358 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalTest.java @@ -79,6 +79,7 @@ public void testSize() throws InterruptedException { final InternalThreadLocal internalThreadLocalString = new InternalThreadLocal(); internalThreadLocalString.set("value"); Assertions.assertEquals(2, InternalThreadLocal.size(), "size method is wrong!"); + InternalThreadLocal.removeAll(); } @Test diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java new file mode 100644 index 00000000000..a17b462de03 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/ThreadlessExecutorTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.threadpool; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; + +public class ThreadlessExecutorTest { + private static ThreadlessExecutor executor; + + static { + URL url = URL.valueOf("dubbo://127.0.0.1:12345"); + ExecutorService sharedExecutor = + ExtensionLoader.getExtensionLoader(ExecutorRepository.class) + .getDefaultExtension().createExecutorIfAbsent(url); + executor = new ThreadlessExecutor(sharedExecutor); + } + + @Test + public void test() throws InterruptedException { + for (int i = 0; i < 10; i++) { + executor.execute(()->{throw new RuntimeException("test");}); + } + + CompletableFuture stubFuture = new CompletableFuture<>(); + executor.setWaitingFuture(stubFuture); + Assertions.assertEquals(executor.getWaitingFuture(),stubFuture); + + executor.waitAndDrain(); + + executor.execute(()->{}); + + executor.waitAndDrain(); + + executor.shutdown(); + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.java new file mode 100644 index 00000000000..b19eb372c01 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepositoryTest.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.dubbo.common.threadpool.manager; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; + +public class ExecutorRepositoryTest { + private ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); + + @Test + public void testGetExecutor() { + testGet(URL.valueOf("dubbo://127.0.0.1:23456")); + testGet(URL.valueOf("dubbo://127.0.0.1:23456?side=consumer")); + + Assertions.assertNotNull(executorRepository.getSharedExecutor()); + Assertions.assertNotNull(executorRepository.getServiceExporterExecutor()); + executorRepository.nextScheduledExecutor(); + } + + private void testGet(URL url) { + ExecutorService executorService = executorRepository.createExecutorIfAbsent(url); + executorService.shutdown(); + executorService = executorRepository.createExecutorIfAbsent(url); + Assertions.assertFalse(executorService.isShutdown()); + + Assertions.assertEquals(executorService, executorRepository.getExecutor(url)); + executorService.shutdown(); + Assertions.assertNotEquals(executorService, executorRepository.getExecutor(url)); + } + + @Test + public void testUpdateExecutor() { + URL url = URL.valueOf("dubbo://127.0.0.1:23456?threads=5"); + ThreadPoolExecutor executorService = (ThreadPoolExecutor) executorRepository.createExecutorIfAbsent(url); + + executorService.setCorePoolSize(3); + executorRepository.updateThreadpool(url, executorService); + + executorService.setCorePoolSize(3); + executorService.setMaximumPoolSize(3); + executorRepository.updateThreadpool(url, executorService); + + executorService.setMaximumPoolSize(20); + executorService.setCorePoolSize(10); + executorRepository.updateThreadpool(url, executorService); + + executorService.setCorePoolSize(10); + executorService.setMaximumPoolSize(10); + executorRepository.updateThreadpool(url, executorService); + + executorService.setCorePoolSize(5); + executorRepository.updateThreadpool(url, executorService); + + + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java index ed737efa11f..e71ea6d43be 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java @@ -17,9 +17,9 @@ package org.apache.dubbo.common.threadpool.support; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport; import org.junit.jupiter.api.Test; +import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; @@ -30,6 +30,25 @@ public void jStackDumpTest() throws InterruptedException { URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=/tmp&version=1.0.0&application=morgan&noValue"); AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url); + try { + abortPolicyWithReport.rejectedExecution(() -> System.out.println("hello"), (ThreadPoolExecutor) Executors.newFixedThreadPool(1)); + } catch (RejectedExecutionException rj) { + // ignore + } + + Thread.sleep(1000); + + } + + @Test + public void jStackDumpTest_dumpDirectoryNotExists_cannotBeCreatedTakeUserHome() throws InterruptedException { + final String dumpDirectory = dumpDirectoryCannotBeCreated(); + + URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=" + + dumpDirectory + + "&version=1.0.0&application=morgan&noValue"); + AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url); + try { abortPolicyWithReport.rejectedExecution(new Runnable() { @Override @@ -42,6 +61,38 @@ public void run() { } Thread.sleep(1000); + } + + private String dumpDirectoryCannotBeCreated() { + final String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + // "con" is one of Windows reserved names, https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + return "con"; + } else { + return "/dev/full/" + UUID.randomUUID().toString(); + } + } + @Test + public void jStackDumpTest_dumpDirectoryNotExists_canBeCreated() throws InterruptedException { + final String dumpDirectory = UUID.randomUUID().toString(); + + URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=" + + dumpDirectory + + "&version=1.0.0&application=morgan&noValue"); + AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url); + + try { + abortPolicyWithReport.rejectedExecution(new Runnable() { + @Override + public void run() { + System.out.println("hello"); + } + }, (ThreadPoolExecutor) Executors.newFixedThreadPool(1)); + } catch (RejectedExecutionException rj) { + // ignore + } + + Thread.sleep(1000); } } \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java index 2754635f0ed..f345970fb0e 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/cached/CachedThreadPoolTest.java @@ -60,14 +60,11 @@ public void getExecutor1() throws Exception { Matchers.instanceOf(AbortPolicyWithReport.class)); final CountDownLatch latch = new CountDownLatch(1); - executor.execute(new Runnable() { - @Override - public void run() { - Thread thread = Thread.currentThread(); - assertThat(thread, instanceOf(InternalThread.class)); - assertThat(thread.getName(), startsWith("demo")); - latch.countDown(); - } + executor.execute(() -> { + Thread thread = Thread.currentThread(); + assertThat(thread, instanceOf(InternalThread.class)); + assertThat(thread.getName(), startsWith("demo")); + latch.countDown(); }); latch.await(); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java index c7d610f2c05..4f4ebfde4f0 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolExecutorTest.java @@ -75,16 +75,13 @@ public void testEagerThreadPool() throws Exception { for (int i = 0; i < 15; i++) { Thread.sleep(50); - executor.execute(new Runnable() { - @Override - public void run() { - System.out.println("thread number in current pool:" + executor.getPoolSize() + ", task number in task queue:" + executor.getQueue() - .size() + " executor size: " + executor.getPoolSize()); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + executor.execute(() -> { + System.out.println("thread number in current pool:" + executor.getPoolSize() + ", task number in task queue:" + executor.getQueue() + .size() + " executor size: " + executor.getPoolSize()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); } }); } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java index 95e41875bcb..cb8e9e2661c 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPoolTest.java @@ -62,14 +62,11 @@ public void getExecutor1() throws Exception { Matchers.instanceOf(AbortPolicyWithReport.class)); final CountDownLatch latch = new CountDownLatch(1); - executor.execute(new Runnable() { - @Override - public void run() { - Thread thread = Thread.currentThread(); - assertThat(thread, instanceOf(InternalThread.class)); - assertThat(thread.getName(), startsWith("demo")); - latch.countDown(); - } + executor.execute(() -> { + Thread thread = Thread.currentThread(); + assertThat(thread, instanceOf(InternalThread.class)); + assertThat(thread.getName(), startsWith("demo")); + latch.countDown(); }); latch.await(); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java index 022351462b7..6e03fabd1d0 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/timer/HashedWheelTimerTest.java @@ -18,55 +18,180 @@ package org.apache.dubbo.common.timer; import org.apache.dubbo.common.utils.NamedThreadFactory; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.lang.ref.WeakReference; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class HashedWheelTimerTest { + private CountDownLatch tryStopTaskCountDownLatch = new CountDownLatch(1); + private CountDownLatch errorTaskCountDownLatch = new CountDownLatch(1); - private class PrintTask implements TimerTask { + private static class EmptyTask implements TimerTask { + @Override + public void run(Timeout timeout) { + } + } + + private static class BlockTask implements TimerTask { + @Override + public void run(Timeout timeout) throws InterruptedException { + this.wait(); + } + } + + private class ErrorTask implements TimerTask { + @Override + public void run(Timeout timeout) { + errorTaskCountDownLatch.countDown(); + throw new RuntimeException("Test"); + } + } + + private class TryStopTask implements TimerTask { + private Timer timer; + + public TryStopTask(Timer timer) { + this.timer = timer; + } @Override public void run(Timeout timeout) { - final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - System.out.println("task :" + LocalDateTime.now().format(formatter)); + Assertions.assertThrows(RuntimeException.class, () -> timer.stop()); + tryStopTaskCountDownLatch.countDown(); } } @Test - public void newTimeout() throws InterruptedException { - final Timer timer = newTimer(); - for (int i = 0; i < 10; i++) { - timer.newTimeout(new PrintTask(), 1, TimeUnit.SECONDS); - Thread.sleep(1000); + public void constructorTest() { + // use weak reference to let gc work every time + // which can check finalize method and reduce memory usage in time + WeakReference timer = new WeakReference<>(new HashedWheelTimer()); + timer = new WeakReference<>(new HashedWheelTimer(100, TimeUnit.MILLISECONDS)); + timer = new WeakReference<>(new HashedWheelTimer(100, TimeUnit.MILLISECONDS, 8)); + + // to cover arg check branches + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + null, + 100, + TimeUnit.MILLISECONDS, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 0, + TimeUnit.MILLISECONDS, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 100, + null, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 100, + TimeUnit.MILLISECONDS, + 0, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + Long.MAX_VALUE, + TimeUnit.MILLISECONDS, + 8, -1); + }); + + Assertions.assertThrows(RuntimeException.class, () -> { + new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 100, + TimeUnit.MILLISECONDS, + Integer.MAX_VALUE, -1); + }); + + for (int i = 0; i < 128; i++) { + // to trigger INSTANCE_COUNT_LIMIT + timer = new WeakReference<>(new HashedWheelTimer()); } - Thread.sleep(5000); + + System.gc(); } @Test - public void stop() throws InterruptedException { - final Timer timer = newTimer(); - for (int i = 0; i < 10; i++) { - timer.newTimeout(new PrintTask(), 5, TimeUnit.SECONDS); - Thread.sleep(100); + public void createTaskTest() throws InterruptedException { + HashedWheelTimer timer = new HashedWheelTimer( + new NamedThreadFactory("dubbo-future-timeout", true), + 10, + TimeUnit.MILLISECONDS, + 8, 8); + + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(null, 5, TimeUnit.SECONDS)); + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(new EmptyTask(), 5, null)); + + Timeout timeout = timer.newTimeout(new ErrorTask(), 10, TimeUnit.MILLISECONDS); + errorTaskCountDownLatch.await(); + Assertions.assertFalse(timeout.cancel()); + Assertions.assertFalse(timeout.isCancelled()); + Assertions.assertNotNull(timeout.toString()); + Assertions.assertEquals(timeout.timer(), timer); + + timeout = timer.newTimeout(new EmptyTask(), 1000, TimeUnit.SECONDS); + timeout.cancel(); + Assertions.assertTrue(timeout.isCancelled()); + + List timeouts = new LinkedList<>(); + for (; timer.pendingTimeouts() < 8; ) { + // to trigger maxPendingTimeouts + timeout = timer.newTimeout(new BlockTask(), -1, TimeUnit.MILLISECONDS); + timeouts.add(timeout); + Assertions.assertNotNull(timeout.toString()); } - //stop timer + Assertions.assertEquals(timer.pendingTimeouts(), 8); + + // this will throw an exception because of maxPendingTimeouts + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(new BlockTask(), 1, TimeUnit.MILLISECONDS)); + + timeout = timeouts.get(2); + // wait until the task expired + Thread.sleep(100); + Assertions.assertTrue(timeout.isExpired()); + timer.stop(); + } + + @Test + public void stopTaskTest() throws InterruptedException { + Timer timer = new HashedWheelTimer(new NamedThreadFactory("dubbo-future-timeout", true)); + timer.newTimeout(new TryStopTask(timer), 10, TimeUnit.MILLISECONDS); + tryStopTaskCountDownLatch.await(); - try { - //this will throw a exception - timer.newTimeout(new PrintTask(), 5, TimeUnit.SECONDS); - } catch (Exception e) { - e.printStackTrace(); + for (int i = 0; i < 8; i++) { + timer.newTimeout(new EmptyTask(), 0, TimeUnit.SECONDS); } - } + // stop timer + timer.stop(); + Assertions.assertTrue(timer.isStop()); + + // this will throw an exception + Assertions.assertThrows(RuntimeException.class, + () -> timer.newTimeout(new EmptyTask(), 5, TimeUnit.SECONDS)); - private Timer newTimer() { - return new HashedWheelTimer( - new NamedThreadFactory("dubbo-future-timeout", true), - 100, - TimeUnit.MILLISECONDS); } } \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java index 36af51d5d78..d65e92ea0fc 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CollectionUtilsTest.java @@ -141,7 +141,7 @@ public void testJoinAll() throws Exception { @Test public void testJoinList() throws Exception { - List list = Arrays.asList(); + List list = emptyList(); assertEquals("", CollectionUtils.join(list, "/")); list = Arrays.asList("x"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java index 85e7180120d..eaae7956032 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/CompatibleTypeUtilsTest.java @@ -84,7 +84,7 @@ public void testCompatibleTypeConvert() throws Exception { result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11T12:24:12.047", java.time.LocalTime.class); assertEquals(DateTimeFormatter.ofPattern("HH:mm:ss").format((java.time.LocalTime) result), "12:24:12"); - result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11T12:24:12.047", java.time.LocalDate.class); + result = CompatibleTypeUtils.compatibleTypeConvert("2011-12-11", java.time.LocalDate.class); assertEquals(DateTimeFormatter.ofPattern("yyyy-MM-dd").format((java.time.LocalDate) result), "2011-12-11"); result = CompatibleTypeUtils.compatibleTypeConvert("ab", char[].class); @@ -219,4 +219,4 @@ public void testCompatibleTypeConvert() throws Exception { } } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java index 1f82a5271f2..2324e27eb29 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ConfigUtilsTest.java @@ -91,7 +91,7 @@ public void testMergeValuesAddDefault() { @Test public void testMergeValuesDeleteDefault() { List merged = ConfigUtils.mergeValues(ThreadPool.class, "-default", asList("fixed", "default.limited", "cached")); - assertEquals(asList(), merged); + assertEquals(Collections.emptyList(), merged); } @Test diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java index 36ceb4d2df9..41c0e03ff32 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultPageTest.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common.utils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.List; @@ -32,6 +33,12 @@ public class DefaultPageTest { @Test public void test() { List data = asList(1, 2, 3, 4, 5); - DefaultPage page = new DefaultPage(0, 1, data.subList(0, 1), data.size()); + DefaultPage page = new DefaultPage<>(0, 1, data.subList(0, 1), data.size()); + Assertions.assertEquals(page.getOffset(), 0); + Assertions.assertEquals(page.getPageSize(), 1); + Assertions.assertEquals(page.getTotalSize(), data.size()); + Assertions.assertEquals(page.getData(), data.subList(0, 1)); + Assertions.assertEquals(page.getTotalPages(), 5); + Assertions.assertTrue(page.hasNext()); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java index 34535241b4b..d7a725089e1 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LFUCacheTest.java @@ -16,16 +16,19 @@ */ package org.apache.dubbo.common.utils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.hamcrest.Matchers.equalTo; +import java.io.IOException; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; public class LFUCacheTest { @Test public void testCacheEviction() throws Exception { - LFUCache cache = new LFUCache(8, 0.8f); + LFUCache cache = new LFUCache<>(8, 0.8f); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); @@ -46,7 +49,7 @@ public void testCacheEviction() throws Exception { @Test public void testCacheRemove() throws Exception { - LFUCache cache = new LFUCache(8, 0.8f); + LFUCache cache = new LFUCache<>(8, 0.8f); cache.put("one", 1); cache.put("two", 2); cache.put("three", 3); @@ -65,8 +68,16 @@ public void testCacheRemove() throws Exception { } @Test - public void testCapacity() throws Exception { - LFUCache cache = new LFUCache(); + public void testDefaultCapacity() throws Exception { + LFUCache cache = new LFUCache<>(); assertThat(cache.getCapacity(), equalTo(1000)); } + + @Test + public void testErrorConstructArguments() throws IOException { + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(0, 0.8f)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(-1, 0.8f)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(100, 0.0f)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(100, -0.1f)); + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java index 203cadb547f..39b60ab8490 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/LogTest.java @@ -18,39 +18,68 @@ package org.apache.dubbo.common.utils; import org.apache.log4j.Level; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.hamcrest.MatcherAssert.assertThat; public class LogTest { @Test public void testLogName() throws Exception { - Log log = new Log(); - log.setLogName("log-name"); - assertThat(log.getLogName(), equalTo("log-name")); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogName("log-name"); + log2.setLogName("log-name"); + log3.setLogName("log-name-other"); + assertThat(log1.getLogName(), equalTo("log-name")); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } @Test public void testLogLevel() throws Exception { - Log log = new Log(); - log.setLogLevel(Level.ALL); - assertThat(log.getLogLevel(), is(Level.ALL)); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogLevel(Level.ALL); + log2.setLogLevel(Level.ALL); + log3.setLogLevel(Level.DEBUG); + assertThat(log1.getLogLevel(), is(Level.ALL)); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } @Test public void testLogMessage() throws Exception { - Log log = new Log(); - log.setLogMessage("log-message"); - assertThat(log.getLogMessage(), equalTo("log-message")); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogMessage("log-message"); + log2.setLogMessage("log-message"); + log3.setLogMessage("log-message-other"); + assertThat(log1.getLogMessage(), equalTo("log-message")); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } @Test public void testLogThread() throws Exception { - Log log = new Log(); - log.setLogThread("log-thread"); - assertThat(log.getLogThread(), equalTo("log-thread")); + Log log1 = new Log(); + Log log2 = new Log(); + Log log3 = new Log(); + log1.setLogThread("log-thread"); + log2.setLogThread("log-thread"); + log3.setLogThread("log-thread-other"); + assertThat(log1.getLogThread(), equalTo("log-thread")); + Assertions.assertEquals(log1, log2); + Assertions.assertEquals(log1.hashCode(), log2.hashCode()); + Assertions.assertNotEquals(log1, log3); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.java index 051363b9815..a5a78cc7659 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MemberUtilsTest.java @@ -18,6 +18,8 @@ import org.junit.jupiter.api.Test; +import static org.apache.dubbo.common.utils.MemberUtils.isPrivate; +import static org.apache.dubbo.common.utils.MemberUtils.isPublic; import static org.apache.dubbo.common.utils.MemberUtils.isStatic; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -34,9 +36,19 @@ public void testIsStatic() throws NoSuchMethodException { assertFalse(isStatic(getClass().getMethod("testIsStatic"))); assertTrue(isStatic(getClass().getMethod("staticMethod"))); + assertTrue(isPrivate(getClass().getDeclaredMethod("privateMethod"))); + assertTrue(isPublic(getClass().getMethod("publicMethod"))); } public static void staticMethod() { } + + private void privateMethod() { + + } + + public void publicMethod() { + + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java index 7cb143eee8c..e8eb7f1c1ba 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java @@ -21,6 +21,17 @@ import java.lang.reflect.Method; +import static org.apache.dubbo.common.utils.MethodUtils.excludedDeclaredClass; +import static org.apache.dubbo.common.utils.MethodUtils.findMethod; +import static org.apache.dubbo.common.utils.MethodUtils.findNearestOverriddenMethod; +import static org.apache.dubbo.common.utils.MethodUtils.findOverriddenMethod; +import static org.apache.dubbo.common.utils.MethodUtils.getAllDeclaredMethods; +import static org.apache.dubbo.common.utils.MethodUtils.getAllMethods; +import static org.apache.dubbo.common.utils.MethodUtils.getDeclaredMethods; +import static org.apache.dubbo.common.utils.MethodUtils.getMethods; +import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod; +import static org.apache.dubbo.common.utils.MethodUtils.overrides; + public class MethodUtilsTest { @Test @@ -53,6 +64,38 @@ public void testIsDeprecated() throws Exception { Assertions.assertFalse(MethodUtils.isDeprecated(MethodTestClazz.class.getMethod("getValue"))); } + @Test + public void testIsMetaMethod() { + boolean containMetaMethod = false; + for (Method method : MethodTestClazz.class.getMethods()) { + if (MethodUtils.isMetaMethod(method)) { + containMetaMethod = true; + } + } + Assertions.assertTrue(containMetaMethod); + } + + @Test + public void testGetMethods() throws NoSuchMethodException { + Assertions.assertTrue(getDeclaredMethods(MethodTestClazz.class, excludedDeclaredClass(String.class)).size() > 0); + Assertions.assertTrue(getMethods(MethodTestClazz.class).size() > 0); + Assertions.assertTrue(getAllDeclaredMethods(MethodTestClazz.class).size() > 0); + Assertions.assertTrue(getAllMethods(MethodTestClazz.class).size() > 0); + Assertions.assertNotNull(findMethod(MethodTestClazz.class, "getValue")); + + MethodTestClazz methodTestClazz = new MethodTestClazz(); + invokeMethod(methodTestClazz, "setValue", "Test"); + Assertions.assertEquals(methodTestClazz.getValue(), "Test"); + + Assertions.assertTrue(overrides(MethodOverrideClazz.class.getMethod("get"), + MethodTestClazz.class.getMethod("get"))); + Assertions.assertEquals(findNearestOverriddenMethod(MethodOverrideClazz.class.getMethod("get")), + MethodTestClazz.class.getMethod("get")); + Assertions.assertEquals(findOverriddenMethod(MethodOverrideClazz.class.getMethod("get"), MethodOverrideClazz.class), + MethodTestClazz.class.getMethod("get")); + + } + public class MethodTestClazz { private String value; @@ -64,10 +107,21 @@ public void setValue(String value) { this.value = value; } + public MethodTestClazz get() { + return this; + } + @Deprecated public Boolean deprecatedMethod() { return true; } } + public class MethodOverrideClazz extends MethodTestClazz { + @Override + public MethodTestClazz get() { + return this; + } + } + } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java index c4f51d72d70..b51b6c9d6bd 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java @@ -35,6 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -193,11 +194,15 @@ public void testToURL() throws Exception { public void testIsValidV6Address() { String saved = System.getProperty("java.net.preferIPv6Addresses", "false"); System.setProperty("java.net.preferIPv6Addresses", "true"); + InetAddress address = NetUtils.getLocalAddress(); - if (address instanceof Inet6Address) { - assertThat(NetUtils.isPreferIPV6Address(), equalTo(true)); - } + boolean isPreferIPV6Address = NetUtils.isPreferIPV6Address(); + + // Restore system property to previous value before executing test System.setProperty("java.net.preferIPv6Addresses", saved); + + assumeTrue(address instanceof Inet6Address); + assertThat(isPreferIPV6Address, equalTo(true)); } /** diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java index 24cb292104d..b093bb21480 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java @@ -133,12 +133,13 @@ public void test_primitive() throws Exception { @Test public void test_pojo() throws Exception { assertObject(new Person()); - assertObject(new SerializablePerson()); + assertObject(new BasicTestData(false, '\0', (byte) 0, (short) 0, 0, 0L, 0F, 0D)); + assertObject(new SerializablePerson(Character.MIN_VALUE, false)); } @Test public void test_has_no_nullary_constructor_pojo() { - assertObject(new User(1,"fibbery")); + assertObject(new User(1, "fibbery")); } @Test @@ -147,7 +148,7 @@ public void test_Map_List_pojo() throws Exception { List list = new ArrayList(); list.add(new Person()); - list.add(new SerializablePerson()); + list.add(new SerializablePerson(Character.MIN_VALUE, false)); map.put("k", list); @@ -198,6 +199,11 @@ public void test_PrimitiveArray() throws Exception { assertArrayObject(new Float[]{37F, -39F, 123456.7F}); assertArrayObject(new Double[]{37D, -39D, 123456.7D}); + + assertObject(new int[][]{{37, -39, 12456}}); + assertObject(new Integer[][][]{{{37, -39, 12456}}}); + + assertArrayObject(new Integer[]{37, -39, 12456}); } @Test @@ -287,7 +293,7 @@ public void testJsonObjectToMap() throws Exception { JSONObject jsonObject = new JSONObject(); jsonObject.put("1", "test"); @SuppressWarnings("unchecked") - Map value = (Map)PojoUtils.realize(jsonObject, + Map value = (Map) PojoUtils.realize(jsonObject, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); method.invoke(new PojoUtilsTest(), value); @@ -303,7 +309,7 @@ public void testListJsonObjectToListMap() throws Exception { List list = new ArrayList<>(1); list.add(jsonObject); @SuppressWarnings("unchecked") - List> result = (List>)PojoUtils.realize( + List> result = (List>) PojoUtils.realize( list, method.getParameterTypes()[0], method.getGenericParameterTypes()[0]); @@ -311,9 +317,11 @@ public void testListJsonObjectToListMap() throws Exception { assertEquals("test", result.get(0).get(1)); } - public void setMap(Map map) {} + public void setMap(Map map) { + } - public void setListMap(List> list) {} + public void setListMap(List> list) { + } @Test public void testException() throws Exception { @@ -752,17 +760,123 @@ public void testRealizeCollectionWithNullElement() { assertEquals(setResult, setStr); } + @Test + public void testMapToPojo() throws Exception { + Map map = new HashMap<>(); + map.put("gender", "male"); + map.put("age", 40); + + List> children = new ArrayList<>(); + Map child = new HashMap<>(); + child.put("gender", "male"); + child.put("age", 15); + children.add(child); + map.put("children", children); + + Map features = new HashMap<>(); + features.put("divorce", false); + features.put("money", 0); + features.put("height", "177cm"); + map.put("features", features); + + Parent parent = PojoUtils.mapToPojo(map, Parent.class); + + assertEquals(parent.gender, "male");; + assertEquals(parent.getAge(), 40); + assertEquals(parent.getChildren().size(), 1); + assertEquals(parent.getChildren().get(0).gender, "male"); + assertEquals(parent.getChildren().get(0).age, 15); + assertNotNull(parent.getFeatures()); + assertEquals(parent.getFeatures().get("divorce"), "false"); + assertEquals(parent.getFeatures().get("money"), "0"); + assertEquals(parent.getFeatures().get("height"), "177cm"); + } + public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } + public static class BasicTestData { + + public boolean a; + public char b; + public byte c; + public short d; + public int e; + public long f; + public float g; + public double h; + + public BasicTestData(boolean a, char b, byte c, short d, int e, long f, float g, double h) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + this.g = g; + this.h = h; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (a ? 1 : 2); + result = prime * result + b; + result = prime * result + c; + result = prime * result + c; + result = prime * result + e; + result = (int) (prime * result + f); + result = (int) (prime * result + g); + result = (int) (prime * result + h); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BasicTestData other = (BasicTestData) obj; + if (a != other.a) { + return false; + } + if (b != other.b) { + return false; + } + if (c != other.c) { + return false; + } + if (e != other.e) { + return false; + } + if (f != other.f) { + return false; + } + if (g != other.g) { + return false; + } + if (h != other.h) { + return false; + } + return true; + } + + } + public static class Parent { public String gender; public String email; String name; int age; Child child; + private List children; private String securityEmail; + private Map features; public static Parent getNewParent() { return new Parent(); @@ -799,6 +913,22 @@ public Child getChild() { public void setChild(Child child) { this.child = child; } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public Map getFeatures() { + return features; + } + + public void setFeatures(Map features) { + this.features = features; + } } public static class Child { @@ -901,4 +1031,4 @@ interface Message { boolean isUrgent(); } -} \ No newline at end of file +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java index 6b6b7f66151..0132d3d05aa 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java @@ -43,6 +43,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; public class ReflectUtilsTest { @Test @@ -256,27 +257,25 @@ public void testIsPublicInstanceField() throws Exception { @Test public void testGetBeanPropertyFields() throws Exception { - Map map = ReflectUtils.getBeanPropertyFields(EmptyClass.class); + EmptyClass emptyClass = new EmptyClass(); + Map map = ReflectUtils.getBeanPropertyFields(emptyClass.getClass()); assertThat(map.size(), is(2)); assertThat(map, hasKey("set")); assertThat(map, hasKey("property")); for (Field f : map.values()) { - if (!f.isAccessible()) { - fail(); - } + assertDoesNotThrow(() -> f.get(emptyClass)); } } @Test public void testGetBeanPropertyReadMethods() throws Exception { - Map map = ReflectUtils.getBeanPropertyReadMethods(EmptyClass.class); + EmptyClass emptyClass = new EmptyClass(); + Map map = ReflectUtils.getBeanPropertyReadMethods(emptyClass.getClass()); assertThat(map.size(), is(2)); assertThat(map, hasKey("set")); assertThat(map, hasKey("property")); for (Method m : map.values()) { - if (!m.isAccessible()) { - fail(); - } + assertDoesNotThrow(() -> m.invoke(emptyClass)); } } @@ -416,18 +415,51 @@ public void testGetReturnTypes () throws Exception{ Assertions.assertEquals("java.lang.String", types1[0].getTypeName()); Assertions.assertEquals("java.lang.String", types1[1].getTypeName()); - Type[] types2 = ReflectUtils.getReturnTypes(clazz.getMethod("getListFuture")); - Assertions.assertEquals("java.util.List", types2[0].getTypeName()); - Assertions.assertEquals("java.util.List", types2[1].getTypeName()); + Type[] types2 = ReflectUtils.getReturnTypes(clazz.getMethod("getT")); + Assertions.assertEquals("java.lang.String", types2[0].getTypeName()); + Assertions.assertEquals("T", types2[1].getTypeName()); + + Type[] types3 = ReflectUtils.getReturnTypes(clazz.getMethod("getS")); + Assertions.assertEquals("java.lang.Object", types3[0].getTypeName()); + Assertions.assertEquals("S", types3[1].getTypeName()); + + Type[] types4 = ReflectUtils.getReturnTypes(clazz.getMethod("getListFuture")); + Assertions.assertEquals("java.util.List", types4[0].getTypeName()); + Assertions.assertEquals("java.util.List", types4[1].getTypeName()); + + Type[] types5 = ReflectUtils.getReturnTypes(clazz.getMethod("getGenericWithUpperFuture")); + // T extends String, the first arg should be the upper bound of param + Assertions.assertEquals("java.lang.String", types5[0].getTypeName()); + Assertions.assertEquals("T", types5[1].getTypeName()); + + Type[] types6 = ReflectUtils.getReturnTypes(clazz.getMethod("getGenericFuture")); + // default upper bound is Object + Assertions.assertEquals("java.lang.Object", types6[0].getTypeName()); + Assertions.assertEquals("S", types6[1].getTypeName()); + } + + @Test + public void testCheckZeroArgConstructor() { + assertTrue(ReflectUtils.checkZeroArgConstructor(String.class)); + assertTrue(ReflectUtils.checkZeroArgConstructor(Bar.class)); + assertFalse(ReflectUtils.checkZeroArgConstructor(Foo4.class)); } - public interface TypeClass { + public interface TypeClass { CompletableFuture getFuture(); String getString(); + T getT(); + + S getS(); + CompletableFuture> getListFuture(); + + CompletableFuture getGenericWithUpperFuture(); + + CompletableFuture getGenericFuture(); } public static class EmptyClass { @@ -518,4 +550,17 @@ public Foo1 hello(Foo2 foo2) { } -} \ No newline at end of file + static class Foo4 { + public Foo4(int i) { + + } + } + + static class Bar { + private Bar() { + + } + } + + +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeClassCheckerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeClassCheckerTest.java new file mode 100644 index 00000000000..b58f3998089 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeClassCheckerTest.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.dubbo.common.utils; + +import org.apache.dubbo.common.constants.CommonConstants; + +import javassist.compiler.Javac; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.Socket; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +public class SerializeClassCheckerTest { + + @BeforeEach + public void setUp() { + SerializeClassChecker.clearInstance(); + } + + @Test + public void testCommon() { + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + + for (int i = 0; i < 10; i++) { + serializeClassChecker.validateClass(List.class.getName()); + serializeClassChecker.validateClass(LinkedList.class.getName()); + serializeClassChecker.validateClass(Integer.class.getName()); + serializeClassChecker.validateClass(int.class.getName()); + + serializeClassChecker.validateClass(List.class.getName().toUpperCase(Locale.ROOT)); + serializeClassChecker.validateClass(LinkedList.class.getName().toUpperCase(Locale.ROOT)); + serializeClassChecker.validateClass(Integer.class.getName().toUpperCase(Locale.ROOT)); + serializeClassChecker.validateClass(int.class.getName().toUpperCase(Locale.ROOT)); + } + + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(Socket.class.getName()); + }); + } + + @Test + public void testAddAllow() { + System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, Socket.class.getName() + "," + Javac.class.getName()); + + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + for (int i = 0; i < 10; i++) { + serializeClassChecker.validateClass(Socket.class.getName()); + serializeClassChecker.validateClass(Javac.class.getName()); + } + + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST); + } + + @Test + public void testAddBlock() { + System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST, LinkedList.class.getName() + "," + Integer.class.getName()); + + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + for (int i = 0; i < 10; i++) { + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(LinkedList.class.getName()); + }); + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(Integer.class.getName()); + }); + } + + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST); + } + + @Test + public void testBlockAll() { + System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL, "true"); + System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, LinkedList.class.getName()); + + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + for (int i = 0; i < 10; i++) { + serializeClassChecker.validateClass(LinkedList.class.getName()); + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(Integer.class.getName()); + }); + } + + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL); + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST); + } +} diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.activate.ActivateWrapperExt1 b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.activate.ActivateWrapperExt1 new file mode 100644 index 00000000000..78e4606d46c --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.activate.ActivateWrapperExt1 @@ -0,0 +1,3 @@ +wrapper1=org.apache.dubbo.common.extension.activate.impl.ActivateWrapperExt1Wrapper +extImp1=org.apache.dubbo.common.extension.activate.impl.ActivateWrapperExt1Impl1 +extImp2=org.apache.dubbo.common.extension.activate.impl.ActivateWrapperExt1Impl2 \ No newline at end of file diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo new file mode 100644 index 00000000000..a346d659f82 --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.extension.wrapper.Demo @@ -0,0 +1,4 @@ +demo=org.apache.dubbo.common.extension.wrapper.impl.DemoImpl +wrapper=org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper +wrapper2=org.apache.dubbo.common.extension.wrapper.impl.DemoWrapper2 +demo2=org.apache.dubbo.common.extension.wrapper.impl.DemoImpl \ No newline at end of file diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java index 29cc14ae3cd..b2bba70b7f9 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java @@ -66,9 +66,7 @@ public ResponseFuture getFuture() { public Object get() throws RemotingException { try { return future.get(); - } catch (InterruptedException e) { - throw new RemotingException(e); - } catch (ExecutionException e) { + } catch (InterruptedException | ExecutionException e) { throw new RemotingException(e); } } @@ -77,11 +75,7 @@ public Object get() throws RemotingException { public Object get(int timeoutInMillis) throws RemotingException { try { return future.get(timeoutInMillis, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - throw new RemotingException(e); - } catch (ExecutionException e) { - throw new RemotingException(e); - } catch (TimeoutException e) { + } catch (InterruptedException | TimeoutException | ExecutionException e) { throw new RemotingException(e); } } diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java b/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java index 24cbfa291a4..1ac9119f541 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/echo/EchoServiceTest.java @@ -23,10 +23,10 @@ import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProxyFactory; +import org.apache.dubbo.rpc.service.EchoService; import org.apache.dubbo.service.DemoService; import org.apache.dubbo.service.DemoServiceImpl; -import com.alibaba.dubbo.rpc.service.EchoService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java b/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java index ec9a08e3de3..7b012d4a26e 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/generic/GenericServiceTest.java @@ -18,6 +18,7 @@ package org.apache.dubbo.generic; +import com.alibaba.dubbo.config.ReferenceConfig; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder; @@ -88,6 +89,29 @@ public void testGeneric2() { exporter.unexport(); } + @Test + public void testGenericCompatible() { + DemoService server = new DemoServiceImpl(); + ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); + Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); + URL url = URL.valueOf("dubbo://127.0.0.1:5342/" + DemoService.class.getName() + "?version=1.0.0&generic=true$timeout=3000"); + Exporter exporter = protocol.export(proxyFactory.getInvoker(server, DemoService.class, url)); + + // simulate normal invoke + ReferenceConfig oldReferenceConfig = new ReferenceConfig<>(); + oldReferenceConfig.setGeneric(true); + oldReferenceConfig.setInterface(DemoService.class.getName()); + oldReferenceConfig.checkAndUpdateSubConfigs(); + Invoker invoker = protocol.refer(oldReferenceConfig.getInterfaceClass(), url); + com.alibaba.dubbo.rpc.service.GenericService client = (com.alibaba.dubbo.rpc.service.GenericService) proxyFactory.getProxy(invoker, true); + + Object result = client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"haha"}); + Assertions.assertEquals("hello haha", result); + + invoker.destroy(); + exporter.unexport(); + } + @Test public void testGenericComplexCompute4FullServiceMetadata() { DemoService server = new DemoServiceImpl(); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java index f38b80ba3e9..7c6e02db639 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java @@ -170,6 +170,7 @@ public ReferenceConfig(Reference reference) { * @see RegistryConstants#SUBSCRIBED_SERVICE_NAMES_KEY * @since 2.7.8 */ + @Deprecated @Parameter(key = SUBSCRIBED_SERVICE_NAMES_KEY) public String getServices() { return services; @@ -181,6 +182,7 @@ public String getServices() { * @return the String {@link List} presenting the Dubbo interface subscribed * @since 2.7.8 */ + @Deprecated @Parameter(excluded = true) public Set getSubscribedServices() { return splitToSet(getServices(), COMMA_SEPARATOR_CHAR); @@ -217,7 +219,7 @@ public synchronized void destroy() { try { invoker.destroy(); } catch (Throwable t) { - logger.warn("Unexpected error occured when destroy invoker of ReferenceConfig(" + url + ").", t); + logger.warn("Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t); } invoker = null; ref = null; @@ -231,9 +233,10 @@ public synchronized void init() { return; } + if (bootstrap == null) { bootstrap = DubboBootstrap.getInstance(); - bootstrap.init(); + bootstrap.initialize(); } checkAndUpdateSubConfigs(); @@ -295,7 +298,8 @@ public synchronized void init() { if (StringUtils.isEmpty(hostToRegistry)) { hostToRegistry = NetUtils.getLocalHost(); } else if (isInvalidLocalHost(hostToRegistry)) { - throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry); + throw new IllegalArgumentException( + "Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry); } map.put(REGISTER_IP_KEY, hostToRegistry); @@ -357,7 +361,10 @@ private T createProxy(Map map) { } } if (urls.isEmpty()) { - throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config to your spring config."); + throw new IllegalStateException( + "No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + + " use dubbo version " + Version.getVersion() + + ", please config to your spring config."); } } } @@ -368,11 +375,33 @@ private T createProxy(Map map) { List> invokers = new ArrayList>(); URL registryURL = null; for (URL url : urls) { - invokers.add(REF_PROTOCOL.refer(interfaceClass, url)); + Invoker referInvoker = REF_PROTOCOL.refer(interfaceClass, url); + if (shouldCheck()) { + if (referInvoker.isAvailable()) { + invokers.add(referInvoker); + } else { + referInvoker.destroy(); + } + } else { + invokers.add(referInvoker); + } + if (UrlUtils.isRegistry(url)) { registryURL = url; // use last registry url } } + + if (shouldCheck() && invokers.size() == 0) { + throw new IllegalStateException("Failed to check the status of the service " + + interfaceName + + ". No provider available for the service " + + (group == null ? "" : group + "/") + + interfaceName + + (version == null ? "" : ":" + version) + + " from the multi registry cluster" + + " use dubbo version " + Version.getVersion()); + } + if (registryURL != null) { // registry url is available // for multi-subscription scenario, use 'zone-aware' policy by default String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME); @@ -380,7 +409,9 @@ private T createProxy(Map map) { invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers)); } else { // not a registry url, must be direct invoke. String cluster = CollectionUtils.isNotEmpty(invokers) - ? (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : Cluster.DEFAULT) + ? + (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : + Cluster.DEFAULT) : Cluster.DEFAULT; invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers)); } @@ -423,11 +454,6 @@ public void checkAndUpdateSubConfigs() { throw new IllegalStateException(" interface not allow null!"); } completeCompoundConfigs(consumer); - if (consumer != null) { - if (StringUtils.isEmpty(registryIds)) { - setRegistryIds(consumer.getRegistryIds()); - } - } // get consumer's global configuration checkDefault(); @@ -452,12 +478,8 @@ public void checkAndUpdateSubConfigs() { checkInterfaceAndMethods(interfaceClass, getMethods()); } - //init serivceMetadata - serviceMetadata.setVersion(getVersion()); - serviceMetadata.setGroup(getGroup()); - serviceMetadata.setDefaultGroup(getGroup()); + initServiceMetadata(consumer); serviceMetadata.setServiceType(getActualInterface()); - serviceMetadata.setServiceInterfaceName(interfaceName); // TODO, uncomment this line once service key is unified serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version)); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java index 356a8978d46..c278951ead0 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java @@ -170,7 +170,7 @@ public void unexport() { try { exporter.unexport(); } catch (Throwable t) { - logger.warn("Unexpected error occured when unexport " + exporter, t); + logger.warn("Unexpected error occurred when unexport " + exporter, t); } } exporters.clear(); @@ -182,10 +182,6 @@ public void unexport() { } public synchronized void export() { - if (!shouldExport()) { - return; - } - if (bootstrap == null) { bootstrap = DubboBootstrap.getInstance(); bootstrap.initialize(); @@ -193,13 +189,14 @@ public synchronized void export() { checkAndUpdateSubConfigs(); - //init serviceMetadata - serviceMetadata.setVersion(getVersion()); - serviceMetadata.setGroup(getGroup()); - serviceMetadata.setDefaultGroup(getGroup()); + initServiceMetadata(provider); serviceMetadata.setServiceType(getInterfaceClass()); - serviceMetadata.setServiceInterfaceName(getInterface()); serviceMetadata.setTarget(getRef()); + serviceMetadata.generateServiceKey(); + + if (!shouldExport()) { + return; + } if (shouldDelay()) { DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS); @@ -304,6 +301,7 @@ protected synchronized void doExport() { path = interfaceName; } doExportUrls(); + bootstrap.setReady(true); } @SuppressWarnings({"unchecked", "rawtypes"}) @@ -326,8 +324,6 @@ private void doExportUrls() { .orElse(path), group, version); // In case user specified path, register service one more time to map it to path. repository.registerService(pathKey, interfaceClass); - // TODO, uncomment this line once service key is unified - serviceMetadata.setServiceKey(pathKey); doExportUrlsFor1Protocol(protocolConfig, registryURLs); } } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java index 603d9d67fdd..3270def2d2a 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java @@ -23,7 +23,6 @@ import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory; import org.apache.dubbo.common.config.configcenter.wrapper.CompositeDynamicConfiguration; import org.apache.dubbo.common.extension.ExtensionLoader; -import org.apache.dubbo.common.lang.ShutdownHookCallback; import org.apache.dubbo.common.lang.ShutdownHookCallbacks; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -95,7 +94,7 @@ import java.util.function.Supplier; import static java.lang.String.format; -import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties; import static org.apache.dubbo.common.config.configcenter.DynamicConfiguration.getDynamicConfiguration; @@ -172,7 +171,7 @@ public class DubboBootstrap extends GenericEventListener { private AtomicBoolean started = new AtomicBoolean(false); - private AtomicBoolean ready = new AtomicBoolean(true); + private AtomicBoolean ready = new AtomicBoolean(false); private AtomicBoolean destroyed = new AtomicBoolean(false); @@ -207,12 +206,7 @@ private DubboBootstrap() { environment = ApplicationModel.getEnvironment(); DubboShutdownHook.getDubboShutdownHook().register(); - ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() { - @Override - public void callback() throws Throwable { - DubboBootstrap.this.destroy(); - } - }); + ShutdownHookCallbacks.INSTANCE.addCallback(DubboBootstrap.this::destroy); } public void unRegisterShutdownHook() { @@ -348,7 +342,7 @@ public DubboBootstrap protocol(String id, Consumer consumerBuil } public DubboBootstrap protocol(ProtocolConfig protocolConfig) { - return protocols(asList(protocolConfig)); + return protocols(singletonList(protocolConfig)); } public DubboBootstrap protocols(List protocolConfigs) { @@ -420,7 +414,7 @@ public DubboBootstrap provider(String id, Consumer builderConsu } public DubboBootstrap provider(ProviderConfig providerConfig) { - return providers(asList(providerConfig)); + return providers(singletonList(providerConfig)); } public DubboBootstrap providers(List providerConfigs) { @@ -444,7 +438,7 @@ public DubboBootstrap consumer(String id, Consumer builderConsu } public DubboBootstrap consumer(ConsumerConfig consumerConfig) { - return consumers(asList(consumerConfig)); + return consumers(singletonList(consumerConfig)); } public DubboBootstrap consumers(List consumerConfigs) { @@ -458,7 +452,7 @@ public DubboBootstrap consumers(List consumerConfigs) { // {@link ConfigCenterConfig} correlative methods public DubboBootstrap configCenter(ConfigCenterConfig configCenterConfig) { - return configCenters(asList(configCenterConfig)); + return configCenters(singletonList(configCenterConfig)); } public DubboBootstrap configCenters(List configCenterConfigs) { @@ -869,7 +863,6 @@ private void loadRemoteConfigs() { * Initialize {@link MetadataService} from {@link WritableMetadataService}'s extension */ private void initMetadataService() { - startMetadataCenter(); this.metadataService = getDefaultExtension(); this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService); } @@ -915,12 +908,16 @@ public DubboBootstrap start() { if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } + ExtensionLoader exts = getExtensionLoader(DubboBootstrapStartStopListener.class); + exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this)); }).start(); } else { ready.set(true); if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } + ExtensionLoader exts = getExtensionLoader(DubboBootstrapStartStopListener.class); + exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this)); } if (logger.isInfoEnabled()) { logger.info(NAME + " has started."); @@ -986,6 +983,7 @@ public boolean isReady() { return ready.get(); } + public DubboBootstrap stop() throws IllegalStateException { destroy(); return this; @@ -1082,8 +1080,12 @@ private void exportServices() { if (exportAsync) { ExecutorService executor = executorRepository.getServiceExporterExecutor(); Future future = executor.submit(() -> { - sc.export(); - exportedServices.add(sc); + try { + sc.export(); + exportedServices.add(sc); + }catch (Throwable t) { + logger.error("export async catch error : " + t.getMessage(), t); + } }); asyncExportingFutures.add(future); } else { @@ -1167,19 +1169,30 @@ private void registerServiceInstance() { // scheduled task for updating Metadata and ServiceInstance executorRepository.nextScheduledExecutor().scheduleAtFixedRate(() -> { - InMemoryWritableMetadataService localMetadataService = (InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension(); - localMetadataService.blockUntilUpdated(); - ServiceInstanceMetadataUtils.refreshMetadataAndInstance(); - }, 0, ConfigurationUtils.get(METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MICROSECONDS); + try { + InMemoryWritableMetadataService localMetadataService = (InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension(); + localMetadataService.blockUntilUpdated(); + ServiceInstanceMetadataUtils.refreshMetadataAndInstance(); + } catch (Throwable e) { + logger.error("refresh metadata and instance failed", e); + } + }, 0, ConfigurationUtils.get(METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MILLISECONDS); } private void doRegisterServiceInstance(ServiceInstance serviceInstance) { //FIXME + if (logger.isInfoEnabled()) { + logger.info("Start publishing metadata to remote center, this only makes sense for applications enabled remote metadata center."); + } publishMetadataToRemote(serviceInstance); + logger.info("Start registering instance address to registry."); getServiceDiscoveries().forEach(serviceDiscovery -> { calInstanceRevision(serviceDiscovery, serviceInstance); + if (logger.isDebugEnabled()) { + logger.info("Start registering instance address to registry" + serviceDiscovery.getUrl() + ", instance " + serviceInstance); + } // register metadata serviceDiscovery.register(serviceInstance); }); @@ -1255,12 +1268,14 @@ public void destroy() { unreferServices(); destroyRegistries(); - DubboShutdownHook.destroyProtocols(); - destroyServiceDiscoveries(); + destroyServiceDiscoveries(); + destroyExecutorRepository(); clear(); shutdown(); release(); + ExtensionLoader exts = getExtensionLoader(DubboBootstrapStartStopListener.class); + exts.getSupportedExtensionInstances().forEach(ext -> ext.onStop(this)); } } finally { destroyLock.unlock(); @@ -1268,6 +1283,10 @@ public void destroy() { } } + private void destroyExecutorRepository() { + ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension().destroyAll(); + } + private void destroyRegistries() { AbstractRegistryFactory.destroyAll(); } @@ -1387,4 +1406,8 @@ private SslConfig getSsl() { ssl.refresh(); return ssl; } + + public void setReady(boolean ready) { + this.ready.set(ready); + } } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java new file mode 100644 index 00000000000..dc7818d72c2 --- /dev/null +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.config.bootstrap; + +import org.apache.dubbo.common.extension.SPI; + +/** + * call on DubboBootstrap start or stop. + * + * @scene 2.7.9 + * @see DubboBootstrap + */ +@SPI +public interface DubboBootstrapStartStopListener { + + void onStart(DubboBootstrap bootstrap); + + void onStop(DubboBootstrap bootstrap); +} diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java index 99af153c301..9608b7f1bbb 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ReferenceBuilder.java @@ -31,7 +31,7 @@ * * @since 2.7 */ -public class ReferenceBuilder extends AbstractReferenceBuilder> { +public class ReferenceBuilder extends AbstractReferenceBuilder, ReferenceBuilder> { /** * The interface name of the reference service */ @@ -74,8 +74,8 @@ public class ReferenceBuilder extends AbstractReferenceBuilder ReferenceBuilder newBuilder() { + return new ReferenceBuilder<>(); } public ReferenceBuilder id(String id) { diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java index cfea5806b90..b02e7c1d55a 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ServiceBuilder.java @@ -29,7 +29,7 @@ * * @since 2.7 */ -public class ServiceBuilder extends AbstractServiceBuilder> { +public class ServiceBuilder extends AbstractServiceBuilder, ServiceBuilder> { /** * The interface name of the exported service */ @@ -69,11 +69,11 @@ public class ServiceBuilder extends AbstractServiceBuilder ServiceBuilder newBuilder() { + return new ServiceBuilder<>(); } - public ServiceBuilder id(String id) { + public ServiceBuilder id(String id) { return super.id(id); } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java index d7d60fc4bcf..fdb011f8953 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java @@ -20,8 +20,6 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.config.ArgumentConfig; -import org.apache.dubbo.config.MethodConfig; import org.apache.dubbo.config.ProtocolConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.ServiceConfig; @@ -31,11 +29,10 @@ import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static java.util.Collections.emptyList; -import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; +import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; /** * {@link MetadataServiceExporter} implementation based on {@link ConfigManager Dubbo configurations}, the clients @@ -77,7 +74,6 @@ public ConfigurableMetadataServiceExporter export() { serviceConfig.setRef(metadataService); serviceConfig.setGroup(getApplicationConfig().getName()); serviceConfig.setVersion(metadataService.version()); - serviceConfig.setMethods(generateMethodConfig()); // export serviceConfig.export(); @@ -97,28 +93,6 @@ public ConfigurableMetadataServiceExporter export() { return this; } - /** - * Generate Method Config for Service Discovery Metadata

    - *

    - * Make {@link MetadataService} support argument callback, - * used to notify {@link org.apache.dubbo.registry.client.ServiceInstance}'s - * metadata change event - * - * @since 3.0 - */ - private List generateMethodConfig() { - MethodConfig methodConfig = new MethodConfig(); - methodConfig.setName("getAndListenServiceDiscoveryMetadata"); - - ArgumentConfig argumentConfig = new ArgumentConfig(); - argumentConfig.setIndex(1); - argumentConfig.setCallback(true); - - methodConfig.setArguments(Collections.singletonList(argumentConfig)); - - return Collections.singletonList(methodConfig); - } - @Override public ConfigurableMetadataServiceExporter unexport() { if (isExported()) { @@ -146,27 +120,10 @@ private List getRegistries() { private ProtocolConfig generateMetadataProtocol() { ProtocolConfig defaultProtocol = new ProtocolConfig(); - Integer port = getApplicationConfig().getMetadataServicePort(); - - if (port == null || port < -1) { - if (logger.isInfoEnabled()) { - logger.info("Metadata Service Port hasn't been set. " + - "Use default protocol defined in protocols."); - } - List defaultProtocols = ApplicationModel.getConfigManager().getDefaultProtocols(); - - if (defaultProtocols.isEmpty()) { - defaultProtocol.setName(DUBBO_PROTOCOL); - defaultProtocol.setPort(-1); - } else { - return defaultProtocols.get(0); - } - - } else { - defaultProtocol.setName(DUBBO_PROTOCOL); - defaultProtocol.setPort(port); - } - + defaultProtocol.setName(DUBBO); + // defaultProtocol.setHost() ? + // auto-increment port + defaultProtocol.setPort(-1); return defaultProtocol; } } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java index 465815713ff..a6e1276394a 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; +import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -48,7 +49,7 @@ import org.apache.dubbo.monitor.MonitorFactory; import org.apache.dubbo.monitor.MonitorService; import org.apache.dubbo.registry.RegistryService; -import org.apache.dubbo.remoting.Codec; +import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.Dispatcher; import org.apache.dubbo.remoting.Transporter; import org.apache.dubbo.remoting.exchange.Exchanger; @@ -61,7 +62,10 @@ import org.apache.dubbo.rpc.cluster.LoadBalance; import org.apache.dubbo.rpc.support.MockInvoker; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -86,9 +90,10 @@ import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; -import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_DUPLICATE_KEY; +import static org.apache.dubbo.common.constants.RegistryConstants.DUBBO_PUBLISH_INTERFACE_DEFAULT_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL; +import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PUBLISH_INTERFACE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL; import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY; @@ -98,6 +103,7 @@ import static org.apache.dubbo.config.Constants.CONTEXTPATH_KEY; import static org.apache.dubbo.config.Constants.DUBBO_IP_TO_REGISTRY; import static org.apache.dubbo.config.Constants.ENVIRONMENT; +import static org.apache.dubbo.config.Constants.IGNORE_CHECK_KEYS; import static org.apache.dubbo.config.Constants.LAYER_KEY; import static org.apache.dubbo.config.Constants.NAME; import static org.apache.dubbo.config.Constants.ORGANIZATION; @@ -167,6 +173,10 @@ public class ConfigValidationUtils { */ private static final Pattern PATTERN_KEY = Pattern.compile("[*,\\-._0-9a-zA-Z]+"); + public static final String IPV6_START_MARK = "["; + + public static final String IPV6_END_MARK = "]"; + public static List loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) { // check && override if necessary @@ -214,7 +224,7 @@ private static List genCompatibleRegistries(List registryList, boolean if (provider) { // for registries enabled service discovery, automatically register interface compatible addresses. if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol()) - && registryURL.getParameter(REGISTRY_DUPLICATE_KEY, false) + && registryURL.getParameter(REGISTRY_PUBLISH_INTERFACE_KEY, ConfigurationUtils.getDynamicGlobalConfiguration().getBoolean(DUBBO_PUBLISH_INTERFACE_DEFAULT_KEY, false)) && registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) { URL interfaceCompatibleRegistryURL = URLBuilder.from(registryURL) .setProtocol(REGISTRY_PROTOCOL) @@ -462,12 +472,12 @@ public static void validateProtocolConfig(ProtocolConfig config) { if (config != null) { String name = config.getName(); checkName("name", name); - checkName(HOST_KEY, config.getHost()); + checkHost(HOST_KEY, config.getHost()); checkPathName("contextpath", config.getContextpath()); if (DUBBO_PROTOCOL.equals(name)) { - checkMultiExtension(Codec.class, CODEC_KEY, config.getCodec()); + checkMultiExtension(Codec2.class, CODEC_KEY, config.getCodec()); checkMultiExtension(Serialization.class, SERIALIZATION_KEY, config.getSerialization()); checkMultiExtension(Transporter.class, SERVER_KEY, config.getServer()); checkMultiExtension(Transporter.class, CLIENT_KEY, config.getClient()); @@ -576,6 +586,22 @@ public static void checkName(String property, String value) { checkProperty(property, value, MAX_LENGTH, PATTERN_NAME); } + public static void checkHost(String property, String value) { + if (StringUtils.isEmpty(value)) { + return; + } + if (value.startsWith(IPV6_START_MARK) && value.endsWith(IPV6_END_MARK)) { + // if the value start with "[" and end with "]", check whether it is IPV6 + try { + InetAddress.getByName(value); + return; + } catch (UnknownHostException e) { + // not a IPv6 string, do nothing, go on to checkName + } + } + checkName(property, value); + } + public static void checkNameHasSymbol(String property, String value) { checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_SYMBOL); } @@ -600,8 +626,14 @@ public static void checkParameterName(Map parameters) { if (CollectionUtils.isEmptyMap(parameters)) { return; } + List ignoreCheckKeys = new ArrayList<>(); + ignoreCheckKeys.add(BACKUP_KEY); + String ignoreCheckKeysStr = parameters.get(IGNORE_CHECK_KEYS); + if (!StringUtils.isBlank(ignoreCheckKeysStr)) { + ignoreCheckKeys.addAll(Arrays.asList(ignoreCheckKeysStr.split(","))); + } for (Map.Entry entry : parameters.entrySet()) { - if (!entry.getKey().equals(BACKUP_KEY)) { + if (!ignoreCheckKeys.contains(entry.getKey())) { checkNameHasSymbol(entry.getKey(), entry.getValue()); } } diff --git a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener index db73041bd43..3d90c3b90ab 100644 --- a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener +++ b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener @@ -1,2 +1 @@ -service-mapping=org.apache.dubbo.config.event.listener.ServiceNameMappingListener -config-logging=org.apache.dubbo.config.event.listener.LoggingEventListener \ No newline at end of file +config-logging=org.apache.dubbo.config.event.listener.LoggingEventListener diff --git a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter index 1b843b6bbc7..2bda3f23b3a 100644 --- a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter +++ b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter @@ -1,3 +1,2 @@ # since 2.7.8 local = org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter -remote = org.apache.dubbo.config.metadata.RemoteMetadataServiceExporter \ No newline at end of file diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java index 041cd750737..8fdbffcaf8e 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java @@ -17,6 +17,7 @@ package org.apache.dubbo.config; import org.apache.dubbo.common.utils.ConfigUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.config.api.Greeting; import org.apache.dubbo.config.support.Parameter; import org.apache.dubbo.config.utils.ConfigValidationUtils; @@ -106,6 +107,15 @@ public void testAppendProperties3() throws Exception { } }*/ + @Test + public void testValidateProtocolConfig() { + ProtocolConfig protocolConfig = new ProtocolConfig(); + protocolConfig.setCodec("exchange"); + protocolConfig.setName("test"); + protocolConfig.setHost("host"); + ConfigValidationUtils.validateProtocolConfig(protocolConfig); + } + @Test public void testAppendParameters1() throws Exception { Map parameters = new HashMap(); @@ -172,12 +182,14 @@ public void checkExtension() throws Exception { @Test public void checkMultiExtension1() throws Exception { - Assertions.assertThrows(IllegalStateException.class, () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,world")); + Assertions.assertThrows(IllegalStateException.class, + () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,world")); } @Test public void checkMultiExtension2() throws Exception { - Assertions.assertThrows(IllegalStateException.class, () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,-world")); + Assertions.assertThrows(IllegalStateException.class, + () -> ConfigValidationUtils.checkMultiExtension(Greeting.class, "hello", "default,-world")); } @Test @@ -902,11 +914,11 @@ protected static void setOsEnv(Map newenv) throws Exception { try { Class processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment"); - theEnvironmentField.setAccessible(true); + ReflectUtils.makeAccessible(theEnvironmentField); Map env = (Map) theEnvironmentField.get(null); env.putAll(newenv); Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment"); - theCaseInsensitiveEnvironmentField.setAccessible(true); + ReflectUtils.makeAccessible(theCaseInsensitiveEnvironmentField); Map cienv = (Map) theCaseInsensitiveEnvironmentField.get(null); cienv.putAll(newenv); } catch (NoSuchFieldException e) { @@ -915,7 +927,7 @@ protected static void setOsEnv(Map newenv) throws Exception { for (Class cl : classes) { if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { Field field = cl.getDeclaredField("m"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); Object obj = field.get(env); Map map = (Map) obj; map.clear(); diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java index 4d157ff1100..95c7a0b9a69 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java @@ -160,6 +160,7 @@ public void testProxy() throws Exception { assertThat(service2.getExportedUrls(), hasSize(1)); assertEquals(2, TestProxyFactory.count); // local injvm and registry protocol, so expected is 2 + TestProxyFactory.count = 0; } diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/rest/UserService.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/rest/UserService.java index fa5b7ae38d1..6c32912760b 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/rest/UserService.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/rest/UserService.java @@ -19,7 +19,7 @@ package org.apache.dubbo.config.bootstrap.rest; -import org.apache.dubbo.rpc.protocol.rest.support.ContentType; +//import org.apache.dubbo.rpc.protocol.rest.support.ContentType; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -29,12 +29,11 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("users") @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) -@Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) +//@Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) @Api(value = "UserService") public interface UserService { diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java index 3190f34b580..c2445573ca2 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java @@ -47,6 +47,7 @@ public class PublishingServiceDefinitionListenerTest { @BeforeEach public void init() { + ApplicationModel.reset(); String metadataType = DEFAULT_METADATA_STORAGE_TYPE; ConfigManager configManager = ApplicationModel.getConfigManager(); ApplicationConfig applicationConfig = new ApplicationConfig("dubbo-demo-provider"); diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java index 33f1a16aef7..83a699f458f 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java @@ -17,15 +17,15 @@ package org.apache.dubbo.config.mock; import org.apache.dubbo.common.URL; -import org.apache.dubbo.registry.client.ServiceDiscovery; +import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import java.util.HashSet; import java.util.Set; -public class MockServiceDiscovery implements ServiceDiscovery { +public class MockServiceDiscovery extends AbstractServiceDiscovery { + private URL registryURL; - private ServiceInstance serviceInstance; @Override public void initialize(URL registryURL) throws Exception { @@ -38,13 +38,13 @@ public void destroy() throws Exception { } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; + public void doRegister(ServiceInstance serviceInstance) { + } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; + public void doUpdate(ServiceInstance serviceInstance) { + } @Override @@ -61,9 +61,4 @@ public Set getServices() { public URL getUrl() { return registryURL; } - - @Override - public ServiceInstance getLocalInstance() { - return serviceInstance; - } } diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/InvokerSideConfigUrlTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/InvokerSideConfigUrlTest.java index c3809633e56..1850b907c57 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/InvokerSideConfigUrlTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/url/InvokerSideConfigUrlTest.java @@ -151,7 +151,7 @@ public void consumerConfUrlTest() { verifyInvokerUrlGeneration(consumerConf, consumerConfTable); } - @Test + //@Test public void refConfUrlTest() { verifyInvokerUrlGeneration(refConf, refConfTable); } diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java index 811b8a91644..ecb95473740 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java @@ -41,6 +41,7 @@ public class MetadataServiceExporterTest { @BeforeAll public static void init() { + ApplicationModel.reset(); ApplicationModel.getConfigManager().setApplication(new ApplicationConfig("Test")); ApplicationModel.getConfigManager().addRegistry(new RegistryConfig("test://1.2.3.4")); ApplicationModel.getConfigManager().addProtocol(new ProtocolConfig("injvm")); diff --git a/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryFactory b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryFactory index 7b8cf68e24d..ded0832eace 100644 --- a/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryFactory +++ b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryFactory @@ -1,2 +1,3 @@ mockregistry=org.apache.dubbo.config.mock.MockRegistryFactory mockprotocol2=org.apache.dubbo.config.mock.MockRegistryFactory2 +test=org.apache.dubbo.config.mock.MockRegistryFactory diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml index 31bfcf4c149..ae7f3bcc1fc 100644 --- a/dubbo-config/dubbo-config-spring/pom.xml +++ b/dubbo-config/dubbo-config-spring/pom.xml @@ -130,12 +130,6 @@ spring-test test - - junit - junit - 4.12 - test - org.apache.tomcat.embed tomcat-embed-core diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java index be9cd718ce3..b7e470f296a 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java @@ -83,17 +83,20 @@ public boolean isSingleton() { * Initializes there Dubbo's Config Beans before @Reference bean autowiring */ private void prepareDubboConfigBeans() { - beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class); - beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class); - beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class); - beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class); - beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class); - beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class); - beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class); - beansOfTypeIncludingAncestors(applicationContext, ConfigCenterBean.class); - beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class); - beansOfTypeIncludingAncestors(applicationContext, MetricsConfig.class); - beansOfTypeIncludingAncestors(applicationContext, SslConfig.class); + // Refactor 2.7.9 + final boolean includeNonSingletons = true; + final boolean allowEagerInit = false; + beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, ConfigCenterBean.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, MetricsConfig.class, includeNonSingletons, allowEagerInit); + beansOfTypeIncludingAncestors(applicationContext, SslConfig.class, includeNonSingletons, allowEagerInit); } @Override diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java index 01186af8510..f12f0d78916 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java @@ -16,22 +16,21 @@ */ package org.apache.dubbo.config.spring.beans.factory.annotation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; import java.lang.annotation.Annotation; import java.util.List; -import static com.alibaba.spring.util.BeanFactoryUtils.getBeans; -import static com.alibaba.spring.util.BeanFactoryUtils.getOptionalBean; +import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getBeans; +import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getOptionalBean; /** * Abstract Configurable {@link Annotation} Bean Builder diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java index be951aea795..15055f7e533 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java @@ -16,14 +16,13 @@ */ package org.apache.dubbo.config.spring.beans.factory.annotation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.dubbo.config.AbstractInterfaceConfig; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ModuleConfig; import org.apache.dubbo.config.MonitorConfig; import org.apache.dubbo.config.RegistryConfig; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.util.Assert; @@ -31,8 +30,8 @@ import java.lang.annotation.Annotation; import java.util.List; -import static com.alibaba.spring.util.BeanFactoryUtils.getBeans; -import static com.alibaba.spring.util.BeanFactoryUtils.getOptionalBean; +import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getBeans; +import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getOptionalBean; /** * An Abstract Builder to build {@link AbstractInterfaceConfig Interface Config} Bean that annotated diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java index 07de2ee6465..9a9dfaca644 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.config.spring.beans.factory.annotation; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Reference; @@ -24,6 +26,7 @@ import org.apache.dubbo.config.spring.ServiceBean; import com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor; +import com.alibaba.spring.util.AnnotationUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -31,15 +34,25 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.util.ObjectUtils; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Map; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; import static com.alibaba.spring.util.AnnotationUtils.getAttribute; import static com.alibaba.spring.util.AnnotationUtils.getAttributes; @@ -56,7 +69,9 @@ * @since 2.5.7 */ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements - ApplicationContextAware { + ApplicationContextAware, ApplicationListener { + + private static final Logger logger = LoggerFactory.getLogger(ReferenceAnnotationBeanPostProcessor.class); /** * The bean name of {@link ReferenceAnnotationBeanPostProcessor} @@ -79,6 +94,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean private ApplicationContext applicationContext; + private static Map> referencedBeanNameIdx = new HashMap<>(); + /** * {@link com.alibaba.dubbo.config.annotation.Reference @com.alibaba.dubbo.config.annotation.Reference} has been supported since 2.7.3 *

    @@ -131,6 +148,8 @@ protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, */ String referenceBeanName = getReferenceBeanName(attributes, injectedType); + referencedBeanNameIdx.computeIfAbsent(referencedBeanName, k -> new TreeSet()).add(referenceBeanName); + ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType); boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes); @@ -212,9 +231,16 @@ private String generateReferenceBeanName(AnnotationAttributes attributes, Class< if (!attributes.isEmpty()) { beanNameBuilder.append('('); for (Map.Entry entry : attributes.entrySet()) { + String value; + if ("parameters".equals(entry.getKey())) { + ArrayList pairs = getParameterPairs(entry); + value = convertAttribute(pairs.stream().sorted().toArray()); + } else { + value = convertAttribute(entry.getValue()); + } beanNameBuilder.append(entry.getKey()) .append('=') - .append(entry.getValue()) + .append(value) .append(','); } // replace the latest "," to be ")" @@ -226,6 +252,38 @@ private String generateReferenceBeanName(AnnotationAttributes attributes, Class< return beanNameBuilder.toString(); } + private ArrayList getParameterPairs(Map.Entry entry) { + String[] entryValues = (String[]) entry.getValue(); + ArrayList pairs = new ArrayList<>(); + // parameters spec is {key1,value1,key2,value2} + for (int i = 0; i < entryValues.length / 2 * 2; i = i + 2) { + pairs.add(entryValues[i] + "=" + entryValues[i + 1]); + } + return pairs; + } + + private String convertAttribute(Object obj) { + if (obj == null) { + return null; + } + if (obj instanceof Annotation) { + AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes((Annotation) obj, true); + for (Map.Entry entry : attributes.entrySet()) { + entry.setValue(convertAttribute(entry.getValue())); + } + return String.valueOf(attributes); + } else if (obj.getClass().isArray()) { + Object[] array = ObjectUtils.toObjectArray(obj); + String[] newArray = new String[array.length]; + for (int i = 0; i < array.length; i++) { + newArray[i] = convertAttribute(array[i]); + } + return Arrays.toString(Arrays.stream(newArray).sorted().toArray()); + } else { + return String.valueOf(obj); + } + } + /** * Is Local Service bean or not? * @@ -344,4 +402,15 @@ public void destroy() throws Exception { this.injectedFieldReferenceBeanCache.clear(); this.injectedMethodReferenceBeanCache.clear(); } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ContextRefreshedEvent) { + referencedBeanNameIdx.entrySet().stream().filter(e -> e.getValue().size() > 1).forEach(e -> { + String logPrefix = e.getKey() + " has " + e.getValue().size() + " reference instances, there are: "; + logger.warn(e.getValue().stream().collect(Collectors.joining(", ", logPrefix, ""))); + }); + referencedBeanNameIdx.clear(); + } + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java index f6aacf4c9e6..813becc72f9 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java @@ -22,7 +22,6 @@ import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.ReferenceBean; - import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationAttributes; @@ -36,9 +35,9 @@ import static com.alibaba.spring.util.AnnotationUtils.getAttribute; import static com.alibaba.spring.util.AnnotationUtils.getAttributes; -import static com.alibaba.spring.util.BeanFactoryUtils.getOptionalBean; import static com.alibaba.spring.util.ObjectUtils.of; import static org.apache.dubbo.config.spring.util.DubboAnnotationUtils.resolveServiceInterfaceClass; +import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getOptionalBean; import static org.springframework.core.annotation.AnnotationAttributes.fromMap; import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java index b1c9a30c3f6..26038d3d135 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java @@ -81,7 +81,7 @@ /** * {@link BeanFactoryPostProcessor} used for processing of {@link Service @Service} annotated classes. it's also the - * infrastructure class of XML {@link BeanDefinitionParser} on <dubbbo:annotation /> + * infrastructure class of XML {@link BeanDefinitionParser} on <dubbo:annotation /> * * @see AnnotationBeanDefinitionParser * @see BeanDefinitionRegistryPostProcessor @@ -239,7 +239,7 @@ private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry regist * {@link Service} Annotation. * * @param scanner {@link ClassPathBeanDefinitionScanner} - * @param packageToScan pachage to scan + * @param packageToScan package to scan * @param registry {@link BeanDefinitionRegistry} * @return non-null * @since 2.5.8 diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigEarlyInitializationPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigEarlyInitializationPostProcessor.java new file mode 100644 index 00000000000..7ec87cafbac --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigEarlyInitializationPostProcessor.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.dubbo.config.spring.beans.factory.config; + +import org.apache.dubbo.config.AbstractConfig; +import org.apache.dubbo.config.context.ConfigManager; + +import com.alibaba.spring.beans.factory.config.GenericBeanPostProcessorAdapter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; +import org.springframework.core.PriorityOrdered; + +import javax.annotation.PostConstruct; + +/** + * Generally, {@link AbstractConfig Dubbo Config} Bean will be added into {@link ConfigManager} on the bean initialization + * life cycle through {@link CommonAnnotationBeanPostProcessor} executing the callback of + * {@link PostConstruct @PostConstruct}. However, the instantiation and initialization of + * {@link AbstractConfig Dubbo Config} Bean could be too early before {@link CommonAnnotationBeanPostProcessor}, e.g, + * execution, thus it's required to register the current instance as a {@link BeanPostProcessor} into + * {@link DefaultListableBeanFactory the BeanFatory} using {@link BeanDefinitionRegistryPostProcessor} as early as + * possible. + * + * @see GenericBeanPostProcessorAdapter + * @since 2.7.9 + */ +public class DubboConfigEarlyInitializationPostProcessor extends GenericBeanPostProcessorAdapter + implements BeanDefinitionRegistryPostProcessor, PriorityOrdered { + + private static final Log logger = LogFactory.getLog(DubboConfigEarlyInitializationPostProcessor.class.getName()); + + public static final String BEAN_NAME = "dubboConfigEarlyInitializationPostProcessor"; + + private DefaultListableBeanFactory beanFactory; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + this.beanFactory = unwrap(registry); + initBeanFactory(); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + if (beanFactory == null) { // try again if postProcessBeanDefinitionRegistry method does not effect. + this.beanFactory = unwrap(beanFactory); + initBeanFactory(); + } + } + + protected void processBeforeInitialization(AbstractConfig config, String beanName) throws BeansException { + + if (this.beanFactory == null) { + if (logger.isErrorEnabled()) { + logger.error("Current Processor is not running in Spring container, next action will be skipped!"); + } + return; + } + + // If CommonAnnotationBeanPostProcessor is already registered, the method addIntoConfigManager() + // will be invoked in Bean life cycle. + if (!hasRegisteredCommonAnnotationBeanPostProcessor()) { + if (logger.isWarnEnabled()) { + logger.warn("CommonAnnotationBeanPostProcessor is not registered yet, " + + "the method addIntoConfigManager() will be invoked directly"); + } + config.addIntoConfigManager(); + } + } + + private DefaultListableBeanFactory unwrap(Object registry) { + if (registry instanceof DefaultListableBeanFactory) { + return (DefaultListableBeanFactory) registry; + } + return null; + } + + private void initBeanFactory() { + if (beanFactory != null) { + // Register itself + if (logger.isInfoEnabled()) { + logger.info("BeanFactory is about to be initialized, trying to resolve the Dubbo Config Beans early " + + "initialization"); + } + beanFactory.addBeanPostProcessor(this); + } + } + + /** + * {@link DefaultListableBeanFactory} has registered {@link CommonAnnotationBeanPostProcessor} or not? + * + * @return if registered, return true, or false + */ + private boolean hasRegisteredCommonAnnotationBeanPostProcessor() { + for (BeanPostProcessor beanPostProcessor : beanFactory.getBeanPostProcessors()) { + if (CommonAnnotationBeanPostProcessor.class.equals(beanPostProcessor.getClass())) { + return true; + } + } + return false; + } + + @Override + public int getOrder() { + return HIGHEST_PRECEDENCE; + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboApplicationListenerRegistrar.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboApplicationListenerRegistrar.java new file mode 100644 index 00000000000..cc45e1a326d --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboApplicationListenerRegistrar.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.context; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; + +import static org.springframework.util.TypeUtils.isAssignable; + +/** + * Dubbo {@link ApplicationListener ApplicationListeners} Registrar + * + * @since 2.7.9 + */ +public class DubboApplicationListenerRegistrar implements ApplicationContextAware { + + /** + * The bean name of {@link DubboApplicationListenerRegistrar} + */ + public static final String BEAN_NAME = "dubboApplicationListenerRegister"; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (!isAssignable(ConfigurableApplicationContext.class, applicationContext.getClass())) { + throw new IllegalArgumentException("The argument of ApplicationContext must be ConfigurableApplicationContext"); + } + addApplicationListeners((ConfigurableApplicationContext) applicationContext); + } + + private void addApplicationListeners(ConfigurableApplicationContext context) { + context.addApplicationListener(createDubboBootstrapApplicationListener(context)); + context.addApplicationListener(createDubboLifecycleComponentApplicationListener(context)); + } + + private ApplicationListener createDubboBootstrapApplicationListener(ConfigurableApplicationContext context) { + return new DubboBootstrapApplicationListener(context); + } + + private ApplicationListener createDubboLifecycleComponentApplicationListener(ConfigurableApplicationContext context) { + return new DubboLifecycleComponentApplicationListener(context); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java index 4a988804bf3..2c5829da91f 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java @@ -18,6 +18,8 @@ import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import com.alibaba.spring.context.OnceApplicationContextEventListener; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ApplicationContextEvent; import org.springframework.context.event.ContextClosedEvent; @@ -30,8 +32,7 @@ * * @since 2.7.5 */ -public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener - implements Ordered { +public class DubboBootstrapApplicationListener extends OnceApplicationContextEventListener implements Ordered { /** * The bean name of {@link DubboBootstrapApplicationListener} @@ -46,8 +47,17 @@ public DubboBootstrapApplicationListener() { this.dubboBootstrap = DubboBootstrap.getInstance(); } + public DubboBootstrapApplicationListener(ApplicationContext applicationContext) { + super(applicationContext); + this.dubboBootstrap = DubboBootstrap.getInstance(); + DubboBootstrapStartStopListenerSpringAdapter.applicationContext = applicationContext; + } + @Override public void onApplicationContextEvent(ApplicationContextEvent event) { + if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) { + DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext(); + } if (event instanceof ContextRefreshedEvent) { onContextRefreshedEvent((ContextRefreshedEvent) event); } else if (event instanceof ContextClosedEvent) { diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java new file mode 100644 index 00000000000..e2e923f439c --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.context; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener; +import org.apache.dubbo.config.spring.context.event.DubboBootstrapStatedEvent; +import org.apache.dubbo.config.spring.context.event.DubboBootstrapStopedEvent; + +import org.springframework.context.ApplicationContext; + +/** + * convert Dubbo bootstrap event to spring environment. + * + * @scene 2.7.9 + */ +public class DubboBootstrapStartStopListenerSpringAdapter implements DubboBootstrapStartStopListener { + + static ApplicationContext applicationContext; + + @Override + public void onStart(DubboBootstrap bootstrap) { + if (applicationContext != null) { + applicationContext.publishEvent(new DubboBootstrapStatedEvent(bootstrap)); + } + } + + @Override + public void onStop(DubboBootstrap bootstrap) { + if (applicationContext != null) { + applicationContext.publishEvent(new DubboBootstrapStopedEvent(bootstrap)); + } + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java index 7e7ae839711..020ce8b39f3 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboLifecycleComponentApplicationListener.java @@ -16,9 +16,9 @@ */ package org.apache.dubbo.config.spring.context; - import org.apache.dubbo.common.context.Lifecycle; +import com.alibaba.spring.context.OnceApplicationContextEventListener; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ApplicationContextEvent; @@ -39,7 +39,7 @@ * @see SmartApplicationListener * @since 2.7.5 */ -public class DubboLifecycleComponentApplicationListener extends OneTimeExecutionApplicationContextEventListener { +public class DubboLifecycleComponentApplicationListener extends OnceApplicationContextEventListener { /** * The bean name of {@link DubboLifecycleComponentApplicationListener} @@ -50,6 +50,13 @@ public class DubboLifecycleComponentApplicationListener extends OneTimeExecution private List lifecycleComponents = emptyList(); + public DubboLifecycleComponentApplicationListener() { + } + + public DubboLifecycleComponentApplicationListener(ApplicationContext applicationContext) { + super(applicationContext); + } + @Override protected void onApplicationContextEvent(ApplicationContextEvent event) { if (event instanceof ContextRefreshedEvent) { diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/OneTimeExecutionApplicationContextEventListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/OneTimeExecutionApplicationContextEventListener.java deleted file mode 100644 index 569a67ec7c4..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/OneTimeExecutionApplicationContextEventListener.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.config.spring.context; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ApplicationContextEvent; - -import java.util.Objects; - -/** - * The abstract class {@link ApplicationListener} for {@link ApplicationContextEvent} guarantees just one-time execution - * and prevents the event propagation in the hierarchical {@link ApplicationContext ApplicationContexts} - * - * @since 2.7.5 - */ -abstract class OneTimeExecutionApplicationContextEventListener implements ApplicationListener, ApplicationContextAware { - - private ApplicationContext applicationContext; - - public final void onApplicationEvent(ApplicationEvent event) { - if (isOriginalEventSource(event) && event instanceof ApplicationContextEvent) { - onApplicationContextEvent((ApplicationContextEvent) event); - } - } - - /** - * The subclass overrides this method to handle {@link ApplicationContextEvent} - * - * @param event {@link ApplicationContextEvent} - */ - protected abstract void onApplicationContextEvent(ApplicationContextEvent event); - - /** - * Is original {@link ApplicationContext} as the event source - * - * @param event {@link ApplicationEvent} - * @return - */ - private boolean isOriginalEventSource(ApplicationEvent event) { - return (applicationContext == null) // Current ApplicationListener is not a Spring Bean, just was added - // into Spring's ConfigurableApplicationContext - || Objects.equals(applicationContext, event.getSource()); - } - - @Override - public final void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - public ApplicationContext getApplicationContext() { - return applicationContext; - } -} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java index 5417acd54a2..1e99c4a61ed 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java @@ -18,7 +18,11 @@ import org.apache.dubbo.config.AbstractConfig; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAttributes; @@ -35,7 +39,9 @@ * @see Ordered * @since 2.5.8 */ -public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar { +public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar, ApplicationContextAware { + + private ConfigurableApplicationContext applicationContext; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { @@ -55,4 +61,12 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B // Since 2.7.6 registerCommonBeans(registry); } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (!(applicationContext instanceof ConfigurableApplicationContext)) { + throw new IllegalArgumentException("The argument of ApplicationContext must be ConfigurableApplicationContext"); + } + this.applicationContext = (ConfigurableApplicationContext) applicationContext; + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java index 1ec03e78bc3..3d5e085b97f 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java @@ -24,6 +24,7 @@ import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; +import com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBinding; import org.springframework.context.annotation.Import; import java.lang.annotation.Documented; @@ -34,7 +35,7 @@ import java.lang.annotation.Target; /** - * As a convenient and multiple {@link EnableDubboConfigBinding} + * As a convenient and multiple {@link EnableConfigurationBeanBinding} * in default behavior , is equal to single bean bindings with below convention prefixes of properties: *

      *
    • {@link ApplicationConfig} binding to property : "dubbo.application"
    • @@ -57,7 +58,7 @@ *
    • {@link ConsumerConfig} binding to property : "dubbo.consumers"
    • *
    * - * @see EnableDubboConfigBinding + * @see EnableConfigurationBeanBinding * @see DubboConfigConfiguration * @see DubboConfigConfigurationRegistrar * @since 2.5.8 diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java new file mode 100644 index 00000000000..0ebaa43a078 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.context.event; + +import org.springframework.context.ApplicationEvent; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; + +/** + * A {@link org.springframework.context.ApplicationEvent} after {@link org.apache.dubbo.config.bootstrap.DubboBootstrap#start()} success + * + * @see org.springframework.context.ApplicationEvent + * @see org.springframework.context.ApplicationListener + * @see org.apache.dubbo.config.bootstrap.DubboBootstrap + * @since 2.7.9 + */ +public class DubboBootstrapStatedEvent extends ApplicationEvent { + + /** + * Create a new ApplicationEvent. + * + * @param bootstrap {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} bootstrap + */ + public DubboBootstrapStatedEvent(DubboBootstrap bootstrap) { + super(bootstrap); + } + + /** + * Get {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} instance + * + * @return non-null + */ + public DubboBootstrap getDubboBootstrap() { + return (DubboBootstrap) super.getSource(); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java new file mode 100644 index 00000000000..ced058b5ec4 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.context.event; + +import org.springframework.context.ApplicationEvent; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; + +/** + * A {@link org.springframework.context.ApplicationEvent} after {@link org.apache.dubbo.config.bootstrap.DubboBootstrap#stop()} success + * + * @see org.springframework.context.ApplicationEvent + * @see org.springframework.context.ApplicationListener + * @see org.apache.dubbo.config.bootstrap.DubboBootstrap + * @since 2.7.9 + */ +public class DubboBootstrapStopedEvent extends ApplicationEvent { + + /** + * Create a new ApplicationEvent. + * + * @param bootstrap {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} bootstrap + */ + public DubboBootstrapStopedEvent(DubboBootstrap bootstrap) { + super(bootstrap); + } + + /** + * Get {@link org.apache.dubbo.config.bootstrap.DubboBootstrap} instance + * + * @return non-null + */ + public DubboBootstrap getDubboBootstrap() { + return (DubboBootstrap) super.getSource(); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java index 1c87e9560f8..e2d1e8ac10c 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java @@ -19,9 +19,13 @@ import org.apache.dubbo.config.AbstractConfig; import org.springframework.beans.MutablePropertyValues; +import org.springframework.validation.BindingResult; import org.springframework.validation.DataBinder; +import org.springframework.validation.FieldError; +import java.util.List; import java.util.Map; +import java.util.stream.IntStream; import static com.alibaba.spring.util.PropertySourcesUtils.getSubProperties; @@ -42,7 +46,25 @@ public void bind(String prefix, C dubboConfig) { MutablePropertyValues propertyValues = new MutablePropertyValues(properties); // Bind dataBinder.bind(propertyValues); + BindingResult bindingResult = dataBinder.getBindingResult(); + if (bindingResult.hasGlobalErrors()) { + throw new RuntimeException("Data bind global error, please check config. config: " + bindingResult.getGlobalError() + ""); + } + if (bindingResult.hasFieldErrors()) { + throw new RuntimeException(buildErrorMsg(bindingResult.getFieldErrors(), prefix, dubboConfig.getClass().getSimpleName())); + } } + private String buildErrorMsg(List errors, String prefix, String config) { + StringBuilder builder = new StringBuilder("Data bind error, please check config. config: " + config + ", prefix: " + prefix + + " , error fields: [" + errors.get(0).getField()); + if (errors.size() > 1) { + IntStream.range(1, errors.size()).forEach(i -> { + builder.append(", " + errors.get(i).getField()); + }); + } + builder.append("]"); + return builder.toString(); + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java index c7b6e77c285..6e08c62108e 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java @@ -21,13 +21,13 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; - -import com.alibaba.spring.util.BeanFactoryUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import java.util.Set; +import static org.apache.dubbo.config.spring.util.DubboBeanUtils.getOptionalBean; + /** * SpringExtensionFactory */ @@ -66,7 +66,7 @@ public T getExtension(Class type, String name) { } for (ApplicationContext context : CONTEXTS) { - T bean = BeanFactoryUtils.getOptionalBean(context, name, type); + T bean = getOptionalBean(context, name, type); if (bean != null) { return bean; } @@ -76,4 +76,5 @@ public T getExtension(Class type, String name) { return null; } + } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java index 084479a83cd..2534bbff9a4 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java @@ -54,7 +54,7 @@ import java.util.Set; import java.util.regex.Pattern; -import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; +import static org.apache.dubbo.common.constants.CommonConstants.HIDDEN_KEY_PREFIX; /** * AbstractBeanDefinitionParser @@ -320,7 +320,7 @@ private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition String value = resolveAttribute(element, "value", parserContext); boolean hide = "true".equals(resolveAttribute(element, "hide", parserContext)); if (hide) { - key = HIDE_KEY_PREFIX + key; + key = HIDDEN_KEY_PREFIX + key; } parameters.put(key, new TypedStringValue(value, String.class)); } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java index ee6a2e47ad7..18be13c4f6e 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java @@ -21,6 +21,7 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.status.Status; import org.apache.dubbo.common.status.StatusChecker; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.config.spring.extension.SpringExtensionFactory; import org.springframework.context.ApplicationContext; @@ -79,9 +80,7 @@ public Status check() { } } if (method != null) { - if (!method.isAccessible()) { - method.setAccessible(true); - } + ReflectUtils.makeAccessible(method); String[] configs = (String[]) method.invoke(context, new Object[0]); if (configs != null && configs.length > 0) { for (String config : configs) { diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboAnnotationUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboAnnotationUtils.java index 7cf35273523..0a4d3adab3e 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboAnnotationUtils.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboAnnotationUtils.java @@ -29,7 +29,7 @@ import static org.springframework.util.StringUtils.hasText; /** - * Dubbbo Annotation Utilities Class + * Dubbo Annotation Utilities Class * * @see org.springframework.core.annotation.AnnotationUtils * @since 2.5.11 diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java index 3b2c31c9539..e6d7d9d2142 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java @@ -16,22 +16,40 @@ */ package org.apache.dubbo.config.spring.util; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigAliasPostProcessor; import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigDefaultPropertyValueBeanPostProcessor; +import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigEarlyInitializationPostProcessor; +import org.apache.dubbo.config.spring.context.DubboApplicationListenerRegistrar; import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener; import org.apache.dubbo.config.spring.context.DubboLifecycleComponentApplicationListener; - +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.BeanNotOfRequiredTypeException; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import static com.alibaba.spring.util.BeanRegistrar.registerInfrastructureBean; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static org.springframework.util.ObjectUtils.isEmpty; /** * Dubbo Bean utilities class * * @since 2.7.6 */ -public interface DubboBeanUtils { +public abstract class DubboBeanUtils { + + private static final Logger logger = LoggerFactory.getLogger(DubboBeanUtils.class); /** * Register the common beans @@ -43,7 +61,7 @@ public interface DubboBeanUtils { * @see DubboLifecycleComponentApplicationListener * @see DubboBootstrapApplicationListener */ - static void registerCommonBeans(BeanDefinitionRegistry registry) { + public static void registerCommonBeans(BeanDefinitionRegistry registry) { // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME, @@ -53,16 +71,91 @@ static void registerCommonBeans(BeanDefinitionRegistry registry) { registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME, DubboConfigAliasPostProcessor.class); + // Since 2.7.9 Register DubboApplicationListenerRegister as an infrastructure Bean + // https://github.com/apache/dubbo/issues/6559 + // Since 2.7.5 Register DubboLifecycleComponentApplicationListener as an infrastructure Bean - registerInfrastructureBean(registry, DubboLifecycleComponentApplicationListener.BEAN_NAME, - DubboLifecycleComponentApplicationListener.class); + // registerInfrastructureBean(registry, DubboLifecycleComponentApplicationListener.BEAN_NAME, + // DubboLifecycleComponentApplicationListener.class); // Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean - registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, - DubboBootstrapApplicationListener.class); + // registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, + // DubboBootstrapApplicationListener.class); + + registerInfrastructureBean(registry, DubboApplicationListenerRegistrar.BEAN_NAME, + DubboApplicationListenerRegistrar.class); // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME, DubboConfigDefaultPropertyValueBeanPostProcessor.class); + + // Since 2.7.9 Register DubboConfigEarlyInitializationPostProcessor as an infrastructure Bean + registerInfrastructureBean(registry, DubboConfigEarlyInitializationPostProcessor.BEAN_NAME, + DubboConfigEarlyInitializationPostProcessor.class); + } + + /** + * Get optional bean by name and type if beanName is not null, or else find by type + * + * @param beanFactory + * @param beanName + * @param beanType + * @param + * @return + */ + public static T getOptionalBean(ListableBeanFactory beanFactory, String beanName, Class beanType) throws BeansException { + if (beanName == null) { + return getOptionalBeanByType(beanFactory, beanType); + } + + T bean = null; + try { + bean = beanFactory.getBean(beanName, beanType); + } catch (NoSuchBeanDefinitionException e) { + // ignore NoSuchBeanDefinitionException + } catch (BeanNotOfRequiredTypeException e) { + // ignore BeanNotOfRequiredTypeException + logger.warn(String.format("bean type not match, name: %s, expected type: %s, actual type: %s", + beanName, beanType.getName(), e.getActualType().getName())); + } + return bean; + } + + private static T getOptionalBeanByType(ListableBeanFactory beanFactory, Class beanType) { + // Issue : https://github.com/alibaba/spring-context-support/issues/20 + String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType, true, false); + if (beanNames == null || beanNames.length == 0) { + return null; + } else if (beanNames.length > 1){ + throw new NoUniqueBeanDefinitionException(beanType, Arrays.asList(beanNames)); + } + return (T) beanFactory.getBean(beanNames[0]); + } + + public static T getBean(ListableBeanFactory beanFactory, String beanName, Class beanType) throws BeansException { + return beanFactory.getBean(beanName, beanType); + } + + /** + * Get beans by names and type + * + * @param beanFactory + * @param beanNames + * @param beanType + * @param + * @return + */ + public static List getBeans(ListableBeanFactory beanFactory, String[] beanNames, Class beanType) throws BeansException { + if (isEmpty(beanNames)) { + return emptyList(); + } + List beans = new ArrayList(beanNames.length); + for (String beanName : beanNames) { + T bean = getBean(beanFactory, beanName, beanType); + if (bean != null) { + beans.add(bean); + } + } + return unmodifiableList(beans); } } diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd index ab229eb49d4..8e5c6d377aa 100644 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd @@ -1070,6 +1070,11 @@ + + + + + @@ -1246,6 +1251,11 @@ + + + + + diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd index a79ebb4670b..339962d7f25 100644 --- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd @@ -1081,6 +1081,11 @@ + + + + + @@ -1257,6 +1262,11 @@ + + + + + diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener new file mode 100644 index 00000000000..8775af29774 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener @@ -0,0 +1 @@ +default=org.apache.dubbo.config.spring.context.DubboBootstrapStartStopListenerSpringAdapter \ No newline at end of file diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java index 62692582e88..d0a30350460 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java @@ -47,31 +47,33 @@ import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.service.GenericService; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.annotation.DirtiesContext; import java.util.Collection; import java.util.List; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_BEAN; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.matchers.JUnitMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * ConfigTest */ -@Ignore +@Disabled +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ConfigTest { @Test @@ -935,7 +937,7 @@ public void testDubboProtocolPortOverride() throws Exception { .service(service) .start(); - Assert.assertEquals(port, service.getExportedUrls().get(0).getPort()); + Assertions.assertEquals(port, service.getExportedUrls().get(0).getPort()); } finally { if (StringUtils.isNotEmpty(dubboPort)) { System.setProperty("dubbo.protocol.dubbo.port", dubboPort); @@ -984,7 +986,7 @@ public void testProtocolRandomPort() throws Exception { try { bootstrap.start(); - Assert.assertEquals(demoService.getExportedUrls().get(0).getPort(), + Assertions.assertEquals(demoService.getExportedUrls().get(0).getPort(), helloService.getExportedUrls().get(0).getPort()); } finally { bootstrap.stop(); @@ -1011,7 +1013,7 @@ public void testReferGenericExport() throws Exception { .reference(ref); try { bootstrap.start(); - Assert.fail(); + Assertions.fail(); } catch (Exception e) { e.printStackTrace(); } finally { @@ -1035,7 +1037,7 @@ public void testGenericServiceConfig() throws Exception { Collection collection = MockRegistryFactory.getCachedRegistry(); MockRegistry registry = (MockRegistry) collection.iterator().next(); URL url = registry.getRegistered().get(0); - Assert.assertEquals(GENERIC_SERIALIZATION_BEAN, url.getParameter(GENERIC_KEY)); + Assertions.assertEquals(GENERIC_SERIALIZATION_BEAN, url.getParameter(GENERIC_KEY)); } finally { MockRegistryFactory.cleanCachedRegistry(); bootstrap.stop(); @@ -1049,7 +1051,7 @@ public void testGenericServiceConfigThroughSpring() throws Exception { ctx.start(); ServiceConfig serviceConfig = (ServiceConfig) ctx.getBean("dubboDemoService"); URL url = (URL) serviceConfig.getExportedUrls().get(0); - Assert.assertEquals(GENERIC_SERIALIZATION_BEAN, url.getParameter(GENERIC_KEY)); + Assertions.assertEquals(GENERIC_SERIALIZATION_BEAN, url.getParameter(GENERIC_KEY)); } finally { ctx.destroy(); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ServiceBeanTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ServiceBeanTest.java index 4def87135d8..fdeb9480b3c 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ServiceBeanTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ServiceBeanTest.java @@ -20,11 +20,13 @@ import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; +import org.springframework.test.annotation.DirtiesContext; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.mockito.Mockito.mock; +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ServiceBeanTest { @Test public void testGetService() { diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/SimpleRegistryService.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/SimpleRegistryService.java index c9b7ac75fb7..53ed1b99a05 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/SimpleRegistryService.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/SimpleRegistryService.java @@ -17,8 +17,6 @@ package org.apache.dubbo.config.spring; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.UrlUtils; @@ -36,7 +34,6 @@ */ public class SimpleRegistryService extends AbstractRegistryService { - private final static Logger logger = LoggerFactory.getLogger(SimpleRegistryService.class); private final ConcurrentMap> remoteRegistered = new ConcurrentHashMap>(); private final ConcurrentMap> remoteListeners = new ConcurrentHashMap>(); private List registries; @@ -139,4 +136,4 @@ public void setRegistries(List registries) { this.registries = registries; } -} \ No newline at end of file +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java index d51fe3c1b48..1c46f772afa 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java @@ -22,12 +22,13 @@ import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.config.spring.api.DemoService; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.util.ReflectionUtils; import org.springframework.validation.DataBinder; @@ -42,6 +43,7 @@ * * @since 2.5.11 */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class AnnotationPropertyValuesAdapterTest { @Test @@ -89,49 +91,49 @@ public Map convert(String[] source) { // System.out.println(referenceBean); - Assert.assertEquals(DemoService.class, referenceBean.getInterfaceClass()); - Assert.assertEquals("org.apache.dubbo.config.spring.api.DemoService", referenceBean.getInterface()); - Assert.assertEquals("1.0.0", referenceBean.getVersion()); - Assert.assertEquals("group", referenceBean.getGroup()); - Assert.assertEquals("dubbo://localhost:12345", referenceBean.getUrl()); - Assert.assertEquals("client", referenceBean.getClient()); - Assert.assertEquals(true, referenceBean.isGeneric()); - Assert.assertNull(referenceBean.isInjvm()); - Assert.assertEquals(false, referenceBean.isCheck()); - Assert.assertEquals(true, referenceBean.isInit()); - Assert.assertEquals(true, referenceBean.getLazy()); - Assert.assertEquals(true, referenceBean.getStubevent()); - Assert.assertEquals("reconnect", referenceBean.getReconnect()); - Assert.assertEquals(true, referenceBean.getSticky()); - - Assert.assertEquals("javassist", referenceBean.getProxy()); - - Assert.assertEquals("stub", referenceBean.getStub()); - Assert.assertEquals("failover", referenceBean.getCluster()); - Assert.assertEquals(Integer.valueOf(1), referenceBean.getConnections()); - Assert.assertEquals(Integer.valueOf(1), referenceBean.getCallbacks()); - Assert.assertEquals("onconnect", referenceBean.getOnconnect()); - Assert.assertEquals("ondisconnect", referenceBean.getOndisconnect()); - Assert.assertEquals("owner", referenceBean.getOwner()); - Assert.assertEquals("layer", referenceBean.getLayer()); - Assert.assertEquals(Integer.valueOf(1), referenceBean.getRetries()); - Assert.assertEquals("random", referenceBean.getLoadbalance()); - Assert.assertEquals(true, referenceBean.isAsync()); - Assert.assertEquals(Integer.valueOf(1), referenceBean.getActives()); - Assert.assertEquals(true, referenceBean.getSent()); - Assert.assertEquals("mock", referenceBean.getMock()); - Assert.assertEquals("validation", referenceBean.getValidation()); - Assert.assertEquals(Integer.valueOf(2), referenceBean.getTimeout()); - Assert.assertEquals("cache", referenceBean.getCache()); - Assert.assertEquals("default,default", referenceBean.getFilter()); - Assert.assertEquals("default,default", referenceBean.getListener()); + Assertions.assertEquals(DemoService.class, referenceBean.getInterfaceClass()); + Assertions.assertEquals("org.apache.dubbo.config.spring.api.DemoService", referenceBean.getInterface()); + Assertions.assertEquals("1.0.0", referenceBean.getVersion()); + Assertions.assertEquals("group", referenceBean.getGroup()); + Assertions.assertEquals("dubbo://localhost:12345", referenceBean.getUrl()); + Assertions.assertEquals("client", referenceBean.getClient()); + Assertions.assertEquals(true, referenceBean.isGeneric()); + Assertions.assertNull(referenceBean.isInjvm()); + Assertions.assertEquals(false, referenceBean.isCheck()); + Assertions.assertEquals(true, referenceBean.isInit()); + Assertions.assertEquals(true, referenceBean.getLazy()); + Assertions.assertEquals(true, referenceBean.getStubevent()); + Assertions.assertEquals("reconnect", referenceBean.getReconnect()); + Assertions.assertEquals(true, referenceBean.getSticky()); + + Assertions.assertEquals("javassist", referenceBean.getProxy()); + + Assertions.assertEquals("stub", referenceBean.getStub()); + Assertions.assertEquals("failover", referenceBean.getCluster()); + Assertions.assertEquals(Integer.valueOf(1), referenceBean.getConnections()); + Assertions.assertEquals(Integer.valueOf(1), referenceBean.getCallbacks()); + Assertions.assertEquals("onconnect", referenceBean.getOnconnect()); + Assertions.assertEquals("ondisconnect", referenceBean.getOndisconnect()); + Assertions.assertEquals("owner", referenceBean.getOwner()); + Assertions.assertEquals("layer", referenceBean.getLayer()); + Assertions.assertEquals(Integer.valueOf(1), referenceBean.getRetries()); + Assertions.assertEquals("random", referenceBean.getLoadbalance()); + Assertions.assertEquals(true, referenceBean.isAsync()); + Assertions.assertEquals(Integer.valueOf(1), referenceBean.getActives()); + Assertions.assertEquals(true, referenceBean.getSent()); + Assertions.assertEquals("mock", referenceBean.getMock()); + Assertions.assertEquals("validation", referenceBean.getValidation()); + Assertions.assertEquals(Integer.valueOf(2), referenceBean.getTimeout()); + Assertions.assertEquals("cache", referenceBean.getCache()); + Assertions.assertEquals("default,default", referenceBean.getFilter()); + Assertions.assertEquals("default,default", referenceBean.getListener()); Map data = new LinkedHashMap(); data.put("key1", "value1"); - Assert.assertEquals(data, referenceBean.getParameters()); + Assertions.assertEquals(data, referenceBean.getParameters()); // Bean compare - Assert.assertNull(referenceBean.getRegistry()); + Assertions.assertNull(referenceBean.getRegistry()); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboReferenceGenericTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboReferenceGenericTest.java new file mode 100644 index 00000000000..d2672b4a9ca --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboReferenceGenericTest.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.dubbo.config.spring.beans.factory.annotation; + +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.dubbo.config.spring.api.HelloService; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.BEAN_NAME; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link DubboReference @DubboReference} of Generic injection test + * + * @see DubboReference + * @since 2.7.9 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration( + classes = { + ServiceAnnotationTestConfiguration.class, + DubboReferenceGenericTest.class + }) +@TestPropertySource(properties = { + "packagesToScan = org.apache.dubbo.config.spring.context.annotation.provider", + "consumer.version = ${demo.service.version}", + "consumer.url = dubbo://127.0.0.1:12345?version=2.5.7", +}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +public class DubboReferenceGenericTest { + + @Bean + public HelloServiceManager helloServiceManager() { + return new HelloServiceManager(); + } + + @Bean(BEAN_NAME) + public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() { + return new ReferenceAnnotationBeanPostProcessor(); + } + + @Bean + public String helloWorld() { + return "Hello,World"; + } + + @Bean + public StringService stringService() { + return new StringService(); + } + + @DubboReference + private HelloService helloService; + + @Autowired + private StringService stringService; + + @Autowired + private HelloServiceManager helloServiceManager; + + @Test + public void test() { + assertEquals("Hello,World", stringService.getS()); + HelloService helloService = helloServiceManager.getService(); + assertEquals("Greeting, Mercy", helloService.sayHello("Mercy")); + } + + + static abstract class AbstractServiceManager { + + @DubboReference + protected S service; + + public S getService() { + return service; + } + } + + static class HelloServiceManager extends AbstractServiceManager { + + } + + static abstract class AbstractService { + + @Autowired + private S s; + + public S getS() { + return s; + } + } + + static class StringService extends AbstractService { + + } + +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java index 766b215a4a3..bc6d45095ca 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java @@ -22,37 +22,39 @@ import org.apache.dubbo.config.spring.annotation.merged.MergedService; import org.apache.dubbo.config.spring.api.DemoService; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field; +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class MergedAnnotationTest { @Test public void testMergedReference() { Field field = ReflectionUtils.findField(TestBean1.class, "demoService"); Reference reference = AnnotatedElementUtils.getMergedAnnotation(field, Reference.class); - Assert.assertEquals("dubbo", reference.group()); - Assert.assertEquals("1.0.0", reference.version()); + Assertions.assertEquals("dubbo", reference.group()); + Assertions.assertEquals("1.0.0", reference.version()); Field field2 = ReflectionUtils.findField(TestBean2.class, "demoService"); Reference reference2 = AnnotatedElementUtils.getMergedAnnotation(field2, Reference.class); - Assert.assertEquals("group", reference2.group()); - Assert.assertEquals("2.0", reference2.version()); + Assertions.assertEquals("group", reference2.group()); + Assertions.assertEquals("2.0", reference2.version()); } @Test public void testMergedService() { Service service1 = AnnotatedElementUtils.getMergedAnnotation(DemoServiceImpl1.class, Service.class); - Assert.assertEquals("dubbo", service1.group()); - Assert.assertEquals("1.0.0", service1.version()); + Assertions.assertEquals("dubbo", service1.group()); + Assertions.assertEquals("1.0.0", service1.version()); Service service2 = AnnotatedElementUtils.getMergedAnnotation(DemoServiceImpl2.class, Service.class); - Assert.assertEquals("group", service2.group()); - Assert.assertEquals("2.0", service2.version()); + Assertions.assertEquals("group", service2.group()); + Assertions.assertEquals("2.0", service2.version()); } @MergedService diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java index 598933d5495..d262850a022 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.config.spring.beans.factory.annotation; +import org.apache.dubbo.config.annotation.Argument; import org.apache.dubbo.config.annotation.Method; import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.spring.ReferenceBean; @@ -27,11 +28,11 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.annotation.Qualifier; @@ -40,22 +41,23 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Collection; import java.util.Map; import static org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.BEAN_NAME; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ReferenceAnnotationBeanPostProcessor} Test * * @since 2.5.7 */ -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { ServiceAnnotationTestConfiguration.class, @@ -68,15 +70,16 @@ "consumer.url = dubbo://127.0.0.1:12345?version=2.5.7", }) @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ReferenceAnnotationBeanPostProcessorTest { - @Before - public void setUp() { + @BeforeAll + public static void setUp() { ApplicationModel.reset(); } - @After - public void tearDown() { + @AfterAll + public static void tearDown() { ApplicationModel.reset(); } @@ -121,6 +124,31 @@ public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor @Reference private HelloService helloService2; + // Instance 1 + @Reference(check = false, parameters = {"a", "2", "b", "1"}, filter = {"echo"}) + private HelloService helloServiceWithArray0; + + // Instance 2 + @Reference(check = false, parameters = {"a", "1", "b", "2"}, filter = {"echo"}) + private HelloService helloServiceWithArray1; + + @Reference(parameters = {"b", "2", "a", "1"}, filter = {"echo"}, check = false) + private HelloService helloServiceWithArray2; + + // Instance 3 + @Reference(check = false, parameters = {"a", "1"}, filter = {"echo"}, methods = {@Method(name = "sayHello", timeout = 100)}) + private HelloService helloServiceWithMethod1; + + @Reference(parameters = {"a", "1"}, filter = {"echo"}, check = false, methods = {@Method(name = "sayHello", timeout = 100)}) + private HelloService helloServiceWithMethod2; + + // Instance 4 + @Reference(parameters = {"a", "1"}, filter = {"echo"}, methods = {@Method(name = "sayHello", arguments = {@Argument(callback = true, type = "String"), @Argument(callback = false, type = "int")}, timeout = 100)}, check = false) + private HelloService helloServiceWithArgument1; + + @Reference(check = false, filter = {"echo"}, parameters = {"a", "1"}, methods = {@Method(name = "sayHello", timeout = 100, arguments = {@Argument(callback = false, type = "int"), @Argument(callback = true, type = "String")})}) + private HelloService helloServiceWithArgument2; + @Test public void test() throws Exception { @@ -131,35 +159,35 @@ public void test() throws Exception { DemoService demoService = testBean.getDemoService(); Map demoServicesMap = context.getBeansOfType(DemoService.class); - Assert.assertNotNull(testBean.getDemoServiceFromAncestor()); - Assert.assertNotNull(testBean.getDemoServiceFromParent()); - Assert.assertNotNull(testBean.getDemoService()); - Assert.assertNotNull(testBean.autowiredDemoService); - Assert.assertEquals(1, demoServicesMap.size()); + Assertions.assertNotNull(testBean.getDemoServiceFromAncestor()); + Assertions.assertNotNull(testBean.getDemoServiceFromParent()); + Assertions.assertNotNull(testBean.getDemoService()); + Assertions.assertNotNull(testBean.autowiredDemoService); + Assertions.assertEquals(1, demoServicesMap.size()); String expectedResult = "Hello,Mercy" + AOP_SUFFIX; - Assert.assertEquals(expectedResult, testBean.autowiredDemoService.sayName("Mercy")); - Assert.assertEquals(expectedResult, demoService.sayName("Mercy")); - Assert.assertEquals("Greeting, Mercy", defaultHelloService.sayHello("Mercy")); - Assert.assertEquals("Hello, Mercy", helloServiceImpl.sayHello("Mercy")); - Assert.assertEquals("Greeting, Mercy", helloService.sayHello("Mercy")); + Assertions.assertEquals(expectedResult, testBean.autowiredDemoService.sayName("Mercy")); + Assertions.assertEquals(expectedResult, demoService.sayName("Mercy")); + Assertions.assertEquals("Greeting, Mercy", defaultHelloService.sayHello("Mercy")); + Assertions.assertEquals("Hello, Mercy", helloServiceImpl.sayHello("Mercy")); + Assertions.assertEquals("Greeting, Mercy", helloService.sayHello("Mercy")); - Assert.assertEquals(expectedResult, testBean.getDemoServiceFromAncestor().sayName("Mercy")); - Assert.assertEquals(expectedResult, testBean.getDemoServiceFromParent().sayName("Mercy")); - Assert.assertEquals(expectedResult, testBean.getDemoService().sayName("Mercy")); + Assertions.assertEquals(expectedResult, testBean.getDemoServiceFromAncestor().sayName("Mercy")); + Assertions.assertEquals(expectedResult, testBean.getDemoServiceFromParent().sayName("Mercy")); + Assertions.assertEquals(expectedResult, testBean.getDemoService().sayName("Mercy")); DemoService myDemoService = context.getBean("my-reference-bean", DemoService.class); - Assert.assertEquals(expectedResult, myDemoService.sayName("Mercy")); + Assertions.assertEquals(expectedResult, myDemoService.sayName("Mercy")); for (DemoService demoService1 : demoServicesMap.values()) { - Assert.assertEquals(myDemoService, demoService1); + Assertions.assertEquals(myDemoService, demoService1); - Assert.assertEquals(expectedResult, demoService1.sayName("Mercy")); + Assertions.assertEquals(expectedResult, demoService1.sayName("Mercy")); } } @@ -175,11 +203,11 @@ public void testGetReferenceBeans() { Collection> referenceBeans = beanPostProcessor.getReferenceBeans(); - Assert.assertEquals(4, referenceBeans.size()); + Assertions.assertEquals(8, referenceBeans.size()); ReferenceBean referenceBean = referenceBeans.iterator().next(); - Assert.assertNotNull(ReferenceConfigCache.getCache().get(referenceBean)); + Assertions.assertNotNull(ReferenceConfigCache.getCache().get(referenceBean)); } @@ -192,13 +220,13 @@ public void testGetInjectedFieldReferenceBeanMap() { Map> referenceBeanMap = beanPostProcessor.getInjectedFieldReferenceBeanMap(); - Assert.assertEquals(3, referenceBeanMap.size()); + Assertions.assertEquals(10, referenceBeanMap.size()); for (Map.Entry> entry : referenceBeanMap.entrySet()) { InjectionMetadata.InjectedElement injectedElement = entry.getKey(); - Assert.assertEquals("com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedFieldElement", + Assertions.assertEquals("com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedFieldElement", injectedElement.getClass().getName()); } @@ -214,13 +242,13 @@ public void testGetInjectedMethodReferenceBeanMap() { Map> referenceBeanMap = beanPostProcessor.getInjectedMethodReferenceBeanMap(); - Assert.assertEquals(2, referenceBeanMap.size()); + Assertions.assertEquals(2, referenceBeanMap.size()); for (Map.Entry> entry : referenceBeanMap.entrySet()) { InjectionMetadata.InjectedElement injectedElement = entry.getKey(); - Assert.assertEquals("com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedMethodElement", + Assertions.assertEquals("com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedMethodElement", injectedElement.getClass().getName()); } @@ -309,12 +337,12 @@ public void testReferenceBeansMethodAnnotation() { Collection> referenceBeans = beanPostProcessor.getReferenceBeans(); - Assert.assertEquals(4, referenceBeans.size()); + Assertions.assertEquals(8, referenceBeans.size()); ReferenceBean referenceBean = referenceBeans.iterator().next(); if ("helloService".equals(referenceBean.getId())) { - Assert.assertNotNull(referenceBean.getMethods()); + Assertions.assertNotNull(referenceBean.getMethods()); } } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java index 60837e274e8..029ae16a3ed 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java @@ -22,17 +22,17 @@ import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.rpc.model.ApplicationModel; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Collections; import java.util.HashMap; @@ -50,8 +50,9 @@ * @see Reference * @since 2.6.4 */ -@RunWith(SpringJUnit4ClassRunner.class) +@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = ReferenceBeanBuilderTest.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ReferenceBeanBuilderTest { @DubboReference( @@ -69,19 +70,20 @@ public class ReferenceBeanBuilderTest { timeout = 3, cache = "cache", filter = {"echo", "generic", "accesslog"}, listener = {"deprecated"}, parameters = {"n1=v1 ", "n2 = v2 ", " n3 = v3 "}, application = "application", - module = "module", consumer = "consumer", monitor = "monitor", registry = {"registry"}, + module = "module", consumer = "consumer", monitor = "monitor", registry = {}, // @since 2.7.3 id = "reference", // @since 2.7.8 - services = {"service1", "service2", "service3", "service2", "service1"} + services = {"service1", "service2", "service3", "service2", "service1"}, + providedBy = {"service1", "service2", "service3"} ) private static final Object TEST_FIELD = new Object(); @Autowired private ApplicationContext context; - @Before - public void init() { + @BeforeAll + public static void init() { ApplicationModel.reset(); } @@ -92,54 +94,55 @@ public void testBuild() throws Exception { ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder.create(attributes, context); beanBuilder.interfaceClass(CharSequence.class); ReferenceBean referenceBean = beanBuilder.build(); - Assert.assertEquals(CharSequence.class, referenceBean.getInterfaceClass()); - Assert.assertEquals("1.0.0", referenceBean.getVersion()); - Assert.assertEquals("TEST_GROUP", referenceBean.getGroup()); - Assert.assertEquals("dubbo://localhost:12345", referenceBean.getUrl()); - Assert.assertEquals("client", referenceBean.getClient()); - Assert.assertEquals(true, referenceBean.isGeneric()); - Assert.assertTrue(referenceBean.isInjvm()); - Assert.assertEquals(false, referenceBean.isCheck()); - Assert.assertFalse(referenceBean.isInit()); - Assert.assertEquals(true, referenceBean.getLazy()); - Assert.assertEquals(true, referenceBean.getStubevent()); - Assert.assertEquals("reconnect", referenceBean.getReconnect()); - Assert.assertEquals(true, referenceBean.getSticky()); - Assert.assertEquals("javassist", referenceBean.getProxy()); - Assert.assertEquals("java.lang.CharSequence", referenceBean.getStub()); - Assert.assertEquals("failover", referenceBean.getCluster()); - Assert.assertEquals(Integer.valueOf(3), referenceBean.getConnections()); - Assert.assertEquals(Integer.valueOf(1), referenceBean.getCallbacks()); - Assert.assertEquals("onconnect", referenceBean.getOnconnect()); - Assert.assertEquals("ondisconnect", referenceBean.getOndisconnect()); - Assert.assertEquals("owner", referenceBean.getOwner()); - Assert.assertEquals("layer", referenceBean.getLayer()); - Assert.assertEquals(Integer.valueOf(1), referenceBean.getRetries()); - Assert.assertEquals("random", referenceBean.getLoadbalance()); - Assert.assertEquals(true, referenceBean.isAsync()); - Assert.assertEquals(Integer.valueOf(3), referenceBean.getActives()); - Assert.assertEquals(true, referenceBean.getSent()); - Assert.assertEquals("mock", referenceBean.getMock()); - Assert.assertEquals("validation", referenceBean.getValidation()); - Assert.assertEquals(Integer.valueOf(3), referenceBean.getTimeout()); - Assert.assertEquals("cache", referenceBean.getCache()); - Assert.assertEquals("echo,generic,accesslog", referenceBean.getFilter()); - Assert.assertEquals("deprecated", referenceBean.getListener()); - Assert.assertEquals("reference", referenceBean.getId()); - Assert.assertEquals(ofSet("service1", "service2", "service3"), referenceBean.getSubscribedServices()); + Assertions.assertEquals(CharSequence.class, referenceBean.getInterfaceClass()); + Assertions.assertEquals("1.0.0", referenceBean.getVersion()); + Assertions.assertEquals("TEST_GROUP", referenceBean.getGroup()); + Assertions.assertEquals("dubbo://localhost:12345", referenceBean.getUrl()); + Assertions.assertEquals("client", referenceBean.getClient()); + Assertions.assertEquals(true, referenceBean.isGeneric()); + Assertions.assertTrue(referenceBean.isInjvm()); + Assertions.assertEquals(false, referenceBean.isCheck()); + Assertions.assertFalse(referenceBean.isInit()); + Assertions.assertEquals(true, referenceBean.getLazy()); + Assertions.assertEquals(true, referenceBean.getStubevent()); + Assertions.assertEquals("reconnect", referenceBean.getReconnect()); + Assertions.assertEquals(true, referenceBean.getSticky()); + Assertions.assertEquals("javassist", referenceBean.getProxy()); + Assertions.assertEquals("java.lang.CharSequence", referenceBean.getStub()); + Assertions.assertEquals("failover", referenceBean.getCluster()); + Assertions.assertEquals(Integer.valueOf(3), referenceBean.getConnections()); + Assertions.assertEquals(Integer.valueOf(1), referenceBean.getCallbacks()); + Assertions.assertEquals("onconnect", referenceBean.getOnconnect()); + Assertions.assertEquals("ondisconnect", referenceBean.getOndisconnect()); + Assertions.assertEquals("owner", referenceBean.getOwner()); + Assertions.assertEquals("layer", referenceBean.getLayer()); + Assertions.assertEquals(Integer.valueOf(1), referenceBean.getRetries()); + Assertions.assertEquals("random", referenceBean.getLoadbalance()); + Assertions.assertEquals(true, referenceBean.isAsync()); + Assertions.assertEquals(Integer.valueOf(3), referenceBean.getActives()); + Assertions.assertEquals(true, referenceBean.getSent()); + Assertions.assertEquals("mock", referenceBean.getMock()); + Assertions.assertEquals("validation", referenceBean.getValidation()); + Assertions.assertEquals(Integer.valueOf(3), referenceBean.getTimeout()); + Assertions.assertEquals("cache", referenceBean.getCache()); + Assertions.assertEquals("echo,generic,accesslog", referenceBean.getFilter()); + Assertions.assertEquals("deprecated", referenceBean.getListener()); + Assertions.assertEquals("reference", referenceBean.getId()); + Assertions.assertEquals(ofSet("service1", "service2", "service3"), referenceBean.getSubscribedServices()); + Assertions.assertEquals("service1,service2,service3", referenceBean.getProvidedBy()); // parameters Map parameters = new HashMap(); parameters.put("n1", "v1"); parameters.put("n2", "v2"); parameters.put("n3", "v3"); - Assert.assertEquals(parameters, referenceBean.getParameters()); + Assertions.assertEquals(parameters, referenceBean.getParameters()); // Asserts Null fields Assertions.assertThrows(IllegalStateException.class, () -> referenceBean.getApplication()); - Assert.assertNull(referenceBean.getModule()); - Assert.assertNull(referenceBean.getConsumer()); - Assert.assertNull(referenceBean.getMonitor()); - Assert.assertEquals(Collections.emptyList(), referenceBean.getRegistries()); + Assertions.assertNull(referenceBean.getModule()); + Assertions.assertNull(referenceBean.getConsumer()); + Assertions.assertNull(referenceBean.getMonitor()); + Assertions.assertEquals(Collections.emptyList(), referenceBean.getRegistries()); } } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java index 171cda3217a..15921a40508 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java @@ -20,18 +20,19 @@ import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.rpc.model.ApplicationModel; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Map; @@ -40,7 +41,7 @@ * * @since 2.5.8 */ -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { ServiceAnnotationTestConfiguration.class, @@ -50,14 +51,15 @@ "provider.package = org.apache.dubbo.config.spring.context.annotation.provider", "packagesToScan = ${provider.package}", }) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ServiceAnnotationBeanPostProcessorTest { - @Before + @BeforeEach public void setUp() { ApplicationModel.reset(); } - @After + @AfterEach public void tearDown() { ApplicationModel.reset(); } @@ -76,19 +78,19 @@ public void test() { Map helloServicesMap = beanFactory.getBeansOfType(HelloService.class); - Assert.assertEquals(2, helloServicesMap.size()); + Assertions.assertEquals(2, helloServicesMap.size()); Map serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class); - Assert.assertEquals(2, serviceBeansMap.size()); + Assertions.assertEquals(2, serviceBeansMap.size()); Map beanPostProcessorsMap = beanFactory.getBeansOfType(ServiceAnnotationBeanPostProcessor.class); - Assert.assertEquals(2, beanPostProcessorsMap.size()); + Assertions.assertEquals(2, beanPostProcessorsMap.size()); - Assert.assertTrue(beanPostProcessorsMap.containsKey("serviceAnnotationBeanPostProcessor")); - Assert.assertTrue(beanPostProcessorsMap.containsKey("serviceAnnotationBeanPostProcessor2")); + Assertions.assertTrue(beanPostProcessorsMap.containsKey("serviceAnnotationBeanPostProcessor")); + Assertions.assertTrue(beanPostProcessorsMap.containsKey("serviceAnnotationBeanPostProcessor2")); } @@ -97,11 +99,11 @@ public void testMethodAnnotation() { Map serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class); - Assert.assertEquals(2, serviceBeansMap.size()); + Assertions.assertEquals(2, serviceBeansMap.size()); ServiceBean demoServiceBean = serviceBeansMap.get("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:2.5.7"); - Assert.assertNotNull(demoServiceBean.getMethods()); + Assertions.assertNotNull(demoServiceBean.getMethods()); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java index 747fc1e2693..2393e5383fa 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java @@ -20,11 +20,12 @@ import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.config.spring.api.DemoService; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.util.ReflectionUtils; import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilderTest.GROUP; @@ -38,6 +39,7 @@ */ @Service(interfaceClass = DemoService.class, group = GROUP, version = VERSION, application = "application", module = "module", registry = {"1", "2", "3"}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ServiceBeanNameBuilderTest { @Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "${dubbo.version}", @@ -50,7 +52,7 @@ public class ServiceBeanNameBuilderTest { private MockEnvironment environment; - @Before + @BeforeEach public void prepare() { environment = new MockEnvironment(); environment.setProperty("dubbo.version", "1.0.0"); @@ -60,10 +62,10 @@ public void prepare() { public void testServiceAnnotation() { Service service = AnnotationUtils.getAnnotation(ServiceBeanNameBuilderTest.class, Service.class); ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, INTERFACE_CLASS, environment); - Assert.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", + Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", builder.build()); - Assert.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", + Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", builder.build()); } @@ -71,7 +73,7 @@ public void testServiceAnnotation() { public void testReferenceAnnotation() { Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(ServiceBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class); ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, INTERFACE_CLASS, environment); - Assert.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", + Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO", builder.build()); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessorTest.java index e6c1d77e698..05234593c2c 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessorTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessorTest.java @@ -20,18 +20,18 @@ import org.apache.dubbo.config.spring.api.HelloService; import org.apache.dubbo.rpc.model.ApplicationModel; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +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.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Map; @@ -40,7 +40,7 @@ * * @since 2.7.7 */ -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { ServiceAnnotationTestConfiguration2.class, @@ -50,14 +50,15 @@ "provider.package = org.apache.dubbo.config.spring.context.annotation.provider", "packagesToScan = ${provider.package}", }) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ServiceClassPostProcessorTest { - @Before + @BeforeEach public void setUp() { ApplicationModel.reset(); } - @After + @BeforeEach public void tearDown() { ApplicationModel.reset(); } @@ -76,19 +77,19 @@ public void test() { Map helloServicesMap = beanFactory.getBeansOfType(HelloService.class); - Assert.assertEquals(2, helloServicesMap.size()); + Assertions.assertEquals(2, helloServicesMap.size()); Map serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class); - Assert.assertEquals(2, serviceBeansMap.size()); + Assertions.assertEquals(2, serviceBeansMap.size()); Map beanPostProcessorsMap = beanFactory.getBeansOfType(ServiceClassPostProcessor.class); - Assert.assertEquals(2, beanPostProcessorsMap.size()); + Assertions.assertEquals(2, beanPostProcessorsMap.size()); - Assert.assertTrue(beanPostProcessorsMap.containsKey("serviceClassPostProcessor")); - Assert.assertTrue(beanPostProcessorsMap.containsKey("serviceClassPostProcessor2")); + Assertions.assertTrue(beanPostProcessorsMap.containsKey("serviceClassPostProcessor")); + Assertions.assertTrue(beanPostProcessorsMap.containsKey("serviceClassPostProcessor2")); } @@ -97,11 +98,11 @@ public void testMethodAnnotation() { Map serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class); - Assert.assertEquals(2, serviceBeansMap.size()); + Assertions.assertEquals(2, serviceBeansMap.size()); ServiceBean demoServiceBean = serviceBeansMap.get("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:2.5.7"); - Assert.assertNotNull(demoServiceBean.getMethods()); + Assertions.assertNotNull(demoServiceBean.getMethods()); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/MultipleServicesWithMethodConfigsTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/MultipleServicesWithMethodConfigsTest.java index d66c8c3e063..d256a46e890 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/MultipleServicesWithMethodConfigsTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/MultipleServicesWithMethodConfigsTest.java @@ -16,19 +16,29 @@ */ package org.apache.dubbo.config.spring.beans.factory.config; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.ImportResource; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MultipleServicesWithMethodConfigsTest.class) @ImportResource(locations = "classpath:/META-INF/spring/multiple-services-with-methods.xml") +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class MultipleServicesWithMethodConfigsTest { + @BeforeAll + public static void setUp() { + ApplicationModel.reset(); + } + @Autowired private ApplicationContext applicationContext; diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactoryTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactoryTest.java index a9861d33c1b..91bd358755b 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactoryTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/config/YamlPropertySourceFactoryTest.java @@ -16,26 +16,28 @@ */ package org.apache.dubbo.config.spring.beans.factory.config; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; /** * {@link YamlPropertySourceFactory} Test * * @since 2.6.5 */ -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @PropertySource(name = "yaml-source", value = {"classpath:/META-INF/dubbo.yml"}, factory = YamlPropertySourceFactory.class) @Configuration @ContextConfiguration(classes = YamlPropertySourceFactoryTest.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class YamlPropertySourceFactoryTest { @Autowired @@ -61,11 +63,11 @@ public class YamlPropertySourceFactoryTest { @Test public void testProperty() { - Assert.assertEquals(isDefault, environment.getProperty("dubbo.consumer.default", Boolean.class)); - Assert.assertEquals(client, environment.getProperty("dubbo.consumer.client", String.class)); - Assert.assertEquals(threadPool, environment.getProperty("dubbo.consumer.threadpool", String.class)); - Assert.assertEquals(coreThreads, environment.getProperty("dubbo.consumer.corethreads", Integer.class)); - Assert.assertEquals(threads, environment.getProperty("dubbo.consumer.threads", Integer.class)); - Assert.assertEquals(queues, environment.getProperty("dubbo.consumer.queues", Integer.class)); + Assertions.assertEquals(isDefault, environment.getProperty("dubbo.consumer.default", Boolean.class)); + Assertions.assertEquals(client, environment.getProperty("dubbo.consumer.client", String.class)); + Assertions.assertEquals(threadPool, environment.getProperty("dubbo.consumer.threadpool", String.class)); + Assertions.assertEquals(coreThreads, environment.getProperty("dubbo.consumer.corethreads", Integer.class)); + Assertions.assertEquals(threads, environment.getProperty("dubbo.consumer.threads", Integer.class)); + Assertions.assertEquals(queues, environment.getProperty("dubbo.consumer.queues", Integer.class)); } } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java index 80a8d40b885..5bf43aa78ed 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboComponentScanRegistrarTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.support.AopUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.transaction.annotation.Transactional; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; @@ -37,6 +38,7 @@ * * @since 2.5.8 */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class DubboComponentScanRegistrarTest { @BeforeEach diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java index 724f132daa6..e515f78ea43 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationTest.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.io.support.ResourcePropertySource; +import org.springframework.test.annotation.DirtiesContext; import java.io.IOException; @@ -35,6 +36,7 @@ * * @since 2.5.8 */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class DubboConfigConfigurationTest { private AnnotationConfigApplicationContext context; diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java index 5817185c693..897b4ee6707 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java @@ -24,11 +24,11 @@ import org.apache.dubbo.config.ProviderConfig; import org.apache.dubbo.config.RegistryConfig; -import org.junit.Assert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.PropertySource; +import org.springframework.test.annotation.DirtiesContext; import java.util.Map; @@ -42,6 +42,7 @@ * * @since 2.5.8 */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class EnableDubboConfigTest { @Test @@ -84,6 +85,8 @@ public void testSingle() { // asserts aliases assertFalse(hasAlias(context, "org.apache.dubbo.config.RegistryConfig#0", "zookeeper")); assertFalse(hasAlias(context, "org.apache.dubbo.config.MonitorConfig#0", "zookeeper")); + + context.close(); } @Test @@ -108,13 +111,15 @@ public void testMultiple() { for (Map.Entry entry : protocolConfigs.entrySet()) { ProtocolConfig protocol = entry.getValue(); - Assert.assertEquals(protocol, context.getBean(protocol.getName(), ProtocolConfig.class)); + Assertions.assertEquals(protocol, context.getBean(protocol.getName(), ProtocolConfig.class)); } // asserts aliases assertTrue(hasAlias(context, "applicationBean2", "dubbo-demo-application2")); assertTrue(hasAlias(context, "applicationBean3", "dubbo-demo-application3")); + context.close(); + } @EnableDubboConfig diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java index d9364d04c67..41dafbb9327 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboTest.java @@ -32,6 +32,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; @@ -46,6 +47,7 @@ * * @since 2.5.8 */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class EnableDubboTest { private AnnotationConfigApplicationContext context; diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/ConsumerConfiguration.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/ConsumerConfiguration.java index a9109f9bfdb..46c2bd07951 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/ConsumerConfiguration.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/consumer/ConsumerConfiguration.java @@ -34,7 +34,7 @@ @PropertySource("META-INF/default.properties") public class ConsumerConfiguration { - private static final String remoteURL = "dubbo://127.0.0.1:12345?version=2.5.7"; + private static final String remoteURL = "injvm://127.0.0.1?version=2.5.7"; /** * Current application configuration, to replace XML config: diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/ProviderConfiguration.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/ProviderConfiguration.java index d168af8bc2b..020f5d2460a 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/ProviderConfiguration.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/ProviderConfiguration.java @@ -68,7 +68,7 @@ public RegistryConfig registryConfig() { /** * Current protocol configuration, to replace XML config: * - * <dubbo:protocol name="dubbo" port="12345"/> + * <dubbo:protocol name="injvm" port="-1"/> * * * @return {@link ProtocolConfig} Bean @@ -76,8 +76,8 @@ public RegistryConfig registryConfig() { @Bean("dubbo") public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); - protocolConfig.setName("dubbo"); - protocolConfig.setPort(12345); + protocolConfig.setName("injvm"); + protocolConfig.setPort(-1); return protocolConfig; } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinderTest.java index 2123b549e22..61971caeb75 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinderTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/properties/DefaultDubboConfigBinderTest.java @@ -22,27 +22,29 @@ import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.rpc.model.ApplicationModel; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; -@RunWith(SpringJUnit4ClassRunner.class) +@ExtendWith(SpringExtension.class) @TestPropertySource(locations = "classpath:/dubbo-binder.properties") @ContextConfiguration(classes = DefaultDubboConfigBinder.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class DefaultDubboConfigBinderTest { - @Before + @BeforeEach public void setUp() { ApplicationModel.reset(); } - @After + @AfterEach public void tearDown() { ApplicationModel.reset(); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactoryTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactoryTest.java index 0e7cbc53dde..9ac0dfa415d 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactoryTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactoryTest.java @@ -29,8 +29,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; @Configuration +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class SpringExtensionFactoryTest { private SpringExtensionFactory springExtensionFactory = new SpringExtensionFactory(); diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java index 8c34e7e5cbe..6570241a4f5 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/issues/Issue6252Test.java @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import org.springframework.test.annotation.DirtiesContext; /** * The test-case for https://github.com/apache/dubbo/issues/6252 @@ -33,6 +34,7 @@ @Configuration @EnableDubboConfig @PropertySource("classpath:/META-INF/issue-6252-test.properties") +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class Issue6252Test { @Bean diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java index 8d8eb7774dc..66f225cd4be 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockRegistryFactory.java @@ -26,20 +26,20 @@ public class MockRegistryFactory implements RegistryFactory { - private static final Map registries = new HashMap(); + private static final Map REGISTRIES = new HashMap(); public static Collection getCachedRegistry() { - return registries.values(); + return REGISTRIES.values(); } public static void cleanCachedRegistry() { - registries.clear(); + REGISTRIES.clear(); } @Override public Registry getRegistry(URL url) { MockRegistry registry = new MockRegistry(url); - registries.put(url, registry); + REGISTRIES.put(url, registry); return registry; } } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java index 1511b0e4ab0..ac060360931 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java @@ -17,15 +17,15 @@ package org.apache.dubbo.config.spring.registry; import org.apache.dubbo.common.URL; -import org.apache.dubbo.registry.client.ServiceDiscovery; +import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import java.util.HashSet; import java.util.Set; -public class MockServiceDiscovery implements ServiceDiscovery { +public class MockServiceDiscovery extends AbstractServiceDiscovery { + private URL registryURL; - private ServiceInstance serviceInstance; @Override public void initialize(URL registryURL) throws Exception { @@ -38,13 +38,13 @@ public void destroy() throws Exception { } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; + public void doRegister(ServiceInstance serviceInstance) { + } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; + public void doUpdate(ServiceInstance serviceInstance) { + } @Override @@ -61,9 +61,4 @@ public Set getServices() { public URL getUrl() { return registryURL; } - - @Override - public ServiceInstance getLocalInstance() { - return serviceInstance; - } } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/nacos/NacosServiceNameTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/nacos/NacosServiceNameTest.java index f3a73807c70..729f6f2f085 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/nacos/NacosServiceNameTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/nacos/nacos/NacosServiceNameTest.java @@ -20,18 +20,20 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.test.annotation.DirtiesContext; import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY; import static org.apache.dubbo.registry.nacos.NacosServiceName.WILDCARD; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link NacosServiceName} Test * * @since 2.7.3 */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class NacosServiceNameTest { private static final String category = DEFAULT_CATEGORY; diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java index 705b890ed48..b56069be59c 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboNamespaceHandlerTest.java @@ -38,6 +38,7 @@ import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.annotation.DirtiesContext; import java.util.Map; @@ -46,6 +47,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class DubboNamespaceHandlerTest { @BeforeEach public void setUp() { @@ -70,6 +72,7 @@ public void testProviderXmlOnConfigurationClass() { applicationContext.register(XmlConfiguration.class); applicationContext.refresh(); testProviderXml(applicationContext); + applicationContext.close(); } @Test @@ -81,6 +84,7 @@ public void testProviderXml() { ctx.start(); testProviderXml(ctx); + ctx.close(); } private void testProviderXml(ApplicationContext context) { @@ -110,6 +114,7 @@ public void testMultiProtocol() { ProtocolConfig dubboProtocolConfig = protocolConfigMap.get("dubbo"); assertThat(dubboProtocolConfig.getPort(), is(20881)); + ctx.close(); } @Test @@ -120,6 +125,7 @@ public void testDefaultProtocol() { ProtocolConfig protocolConfig = ctx.getBean(ProtocolConfig.class); protocolConfig.refresh(); assertThat(protocolConfig.getName(), is("dubbo")); + ctx.close(); } @Test @@ -134,6 +140,7 @@ public void testCustomParameter() { ServiceBean serviceBean = ctx.getBean(ServiceBean.class); assertThat(serviceBean.getParameters().size(), is(1)); assertThat(serviceBean.getParameters().get("service-paramA"), is("service-paramA")); + ctx.close(); } @@ -143,6 +150,7 @@ public void testDelayFixedTime() { ctx.start(); assertThat(ctx.getBean(ServiceBean.class).getDelay(), is(300)); + ctx.close(); } @Test @@ -153,6 +161,7 @@ public void testTimeoutConfig() { Map providerConfigMap = ctx.getBeansOfType(ProviderConfig.class); assertThat(providerConfigMap.get("org.apache.dubbo.config.ProviderConfig").getTimeout(), is(2000)); + ctx.close(); } @Test @@ -161,6 +170,7 @@ public void testMonitor() { ctx.start(); assertThat(ctx.getBean(MonitorConfig.class), not(nullValue())); + ctx.close(); } // @Test @@ -186,6 +196,7 @@ public void testModuleInfo() { ModuleConfig moduleConfig = ctx.getBean(ModuleConfig.class); assertThat(moduleConfig.getName(), is("test-module")); + ctx.close(); } @Test @@ -205,5 +216,6 @@ public void testProperty() { String prefix = ((DemoServiceImpl) serviceBean.getRef()).getPrefix(); assertThat(prefix, is("welcome:")); + ctx.close(); } } \ No newline at end of file diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java index 6deb2abaf5f..72862bf9356 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/GenericServiceTest.java @@ -20,29 +20,32 @@ import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.rpc.model.ApplicationModel; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.ImportResource; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; -@RunWith(SpringRunner.class) + +@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = GenericServiceTest.class) @ImportResource(locations = "classpath:/META-INF/spring/dubbo-generic-consumer.xml") +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class GenericServiceTest { - @Before + @BeforeEach public void setUp() { ApplicationModel.reset(); } - @After + @AfterEach public void tearDown() { ApplicationModel.reset(); } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java index b12d2d4d97c..b24c94109e7 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/DataSourceStatusCheckerTest.java @@ -27,6 +27,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; +import org.springframework.test.annotation.DirtiesContext; import javax.sql.DataSource; import java.sql.Connection; @@ -42,6 +43,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.MockitoAnnotations.initMocks; +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class DataSourceStatusCheckerTest { private DataSourceStatusChecker dataSourceStatusChecker; diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java index e3386e2588d..c4f2f93a7b3 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java @@ -20,14 +20,15 @@ import org.apache.dubbo.config.spring.ServiceBean; import org.apache.dubbo.config.spring.extension.SpringExtensionFactory; -import org.junit.Assert; 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.Mock; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; import org.springframework.context.Lifecycle; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.web.context.support.GenericWebApplicationContext; import static org.hamcrest.CoreMatchers.is; @@ -36,6 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.MockitoAnnotations.initMocks; +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class SpringStatusCheckerTest { private SpringStatusChecker springStatusChecker; @@ -99,6 +101,6 @@ public void testGenericWebApplicationContext() { SpringExtensionFactory.addApplicationContext(context); SpringStatusChecker checker = new SpringStatusChecker(); Status status = checker.check(); - Assert.assertEquals(Status.Level.UNKNOWN, status.getLevel()); + Assertions.assertEquals(Status.Level.UNKNOWN, status.getLevel()); } } diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml index 96ffe7f69c6..d106e2e22f1 100644 --- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml +++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-generic-consumer.xml @@ -31,6 +31,6 @@ - - \ No newline at end of file + diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml index 9054c4f2ec6..d7948cdc86f 100644 --- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml +++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-consumer-context.xml @@ -10,18 +10,18 @@ - - - - + - - \ No newline at end of file + diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml index 207ab7b6767..846c5ffc59a 100644 --- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml +++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/spring/dubbo-nacos-provider-context.xml @@ -12,8 +12,8 @@ - - - \ No newline at end of file + + diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java index f1911787768..c2d1b6b2ad1 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java @@ -42,7 +42,7 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; -import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java index ca48bd6b9b9..b14f172bb91 100644 --- a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java @@ -20,12 +20,11 @@ import org.apache.dubbo.common.config.configcenter.ConfigChangeType; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; -import com.ctrip.framework.apollo.mockserver.EmbeddedApollo; import com.google.common.util.concurrent.SettableFuture; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.FileOutputStream; import java.io.IOException; @@ -51,13 +50,13 @@ public class ApolloDynamicConfigurationTest { /** * The constant embeddedApollo. */ - @ClassRule - public static EmbeddedApollo embeddedApollo = new EmbeddedApollo(); + @RegisterExtension + public static EmbeddedApolloJunit5 embeddedApollo = new EmbeddedApolloJunit5(); /** * Sets up. */ - @Before + @BeforeEach public void setUp() { String apolloUrl = System.getProperty("apollo.configService"); String urlForDubbo = "apollo://" + apolloUrl.substring(apolloUrl.lastIndexOf("/") + 1) + "/org.apache.dubbo.apollo.testService?namespace=dubbo&check=true"; @@ -183,7 +182,7 @@ private static void putMockData(String key, String value, String fileName) { /** * Tear down. */ - @After + @AfterEach public void tearDown() { } diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java new file mode 100644 index 00000000000..425399a36e8 --- /dev/null +++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/EmbeddedApolloJunit5.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.configcenter.support.apollo; + +import com.ctrip.framework.apollo.build.ApolloInjector; +import com.ctrip.framework.apollo.core.dto.ApolloConfig; +import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification; +import com.ctrip.framework.apollo.core.utils.ResourceUtils; +import com.ctrip.framework.apollo.internals.ConfigServiceLocator; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EmbeddedApolloJunit5 implements BeforeAllCallback, AfterAllCallback { + private static final Logger logger = LoggerFactory.getLogger(EmbeddedApolloJunit5.class); + private static final Type notificationType = new TypeToken>() { + }.getType(); + + private static Method CONFIG_SERVICE_LOCATOR_CLEAR; + private static ConfigServiceLocator CONFIG_SERVICE_LOCATOR; + + private static final Gson GSON = new Gson(); + private final Map> addedOrModifiedPropertiesOfNamespace = Maps.newConcurrentMap(); + private final Map> deletedKeysOfNamespace = Maps.newConcurrentMap(); + + private MockWebServer server; + + static { + try { + System.setProperty("apollo.longPollingInitialDelayInMills", "0"); + CONFIG_SERVICE_LOCATOR = ApolloInjector.getInstance(ConfigServiceLocator.class); + CONFIG_SERVICE_LOCATOR_CLEAR = ConfigServiceLocator.class.getDeclaredMethod("initConfigServices"); + CONFIG_SERVICE_LOCATOR_CLEAR.setAccessible(true); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + private void clear() throws Exception { + resetOverriddenProperties(); + } + + private void mockConfigServiceUrl(String url) throws Exception { + System.setProperty("apollo.configService", url); + + CONFIG_SERVICE_LOCATOR_CLEAR.invoke(CONFIG_SERVICE_LOCATOR); + } + + private String loadConfigFor(String namespace) { + String filename = String.format("mockdata-%s.properties", namespace); + final Properties prop = ResourceUtils.readConfigFile(filename, new Properties()); + Map configurations = Maps.newHashMap(); + for (String propertyName : prop.stringPropertyNames()) { + configurations.put(propertyName, prop.getProperty(propertyName)); + } + ApolloConfig apolloConfig = new ApolloConfig("someAppId", "someCluster", namespace, "someReleaseKey"); + + Map mergedConfigurations = mergeOverriddenProperties(namespace, configurations); + apolloConfig.setConfigurations(mergedConfigurations); + return GSON.toJson(apolloConfig); + } + + private String mockLongPollBody(String notificationsStr) { + List oldNotifications = GSON.fromJson(notificationsStr, notificationType); + List newNotifications = new ArrayList<>(); + for (ApolloConfigNotification notification : oldNotifications) { + newNotifications + .add(new ApolloConfigNotification(notification.getNamespaceName(), notification.getNotificationId() + 1)); + } + return GSON.toJson(newNotifications); + } + + /** + * Incorporate user modifications to namespace + */ + private Map mergeOverriddenProperties(String namespace, Map configurations) { + if (addedOrModifiedPropertiesOfNamespace.containsKey(namespace)) { + configurations.putAll(addedOrModifiedPropertiesOfNamespace.get(namespace)); + } + if (deletedKeysOfNamespace.containsKey(namespace)) { + for (String k : deletedKeysOfNamespace.get(namespace)) { + configurations.remove(k); + } + } + return configurations; + } + + /** + * Add new property or update existed property + */ + public void addOrModifyProperty(String namespace, String someKey, String someValue) { + if (addedOrModifiedPropertiesOfNamespace.containsKey(namespace)) { + addedOrModifiedPropertiesOfNamespace.get(namespace).put(someKey, someValue); + } else { + Map m = Maps.newConcurrentMap(); + m.put(someKey, someValue); + addedOrModifiedPropertiesOfNamespace.put(namespace, m); + } + } + + /** + * Delete existed property + */ + public void deleteProperty(String namespace, String someKey) { + if (deletedKeysOfNamespace.containsKey(namespace)) { + deletedKeysOfNamespace.get(namespace).add(someKey); + } else { + Set m = Sets.newConcurrentHashSet(); + m.add(someKey); + deletedKeysOfNamespace.put(namespace, m); + } + } + + /** + * reset overridden properties + */ + public void resetOverriddenProperties() { + addedOrModifiedPropertiesOfNamespace.clear(); + deletedKeysOfNamespace.clear(); + } + + @Override + public void afterAll(ExtensionContext extensionContext) throws Exception { + try { + clear(); + server.close(); + } catch (Exception e) { + logger.error("stop apollo server error", e); + } + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + clear(); + server = new MockWebServer(); + final Dispatcher dispatcher = new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + if (request.getPath().startsWith("/notifications/v2")) { + String notifications = request.getRequestUrl().queryParameter("notifications"); + return new MockResponse().setResponseCode(200).setBody(mockLongPollBody(notifications)); + } + if (request.getPath().startsWith("/configs")) { + List pathSegments = request.getRequestUrl().pathSegments(); + // appId and cluster might be used in the future + String appId = pathSegments.get(1); + String cluster = pathSegments.get(2); + String namespace = pathSegments.get(3); + return new MockResponse().setResponseCode(200).setBody(loadConfigFor(namespace)); + } + return new MockResponse().setResponseCode(404); + } + }; + + server.setDispatcher(dispatcher); + server.start(); + + mockConfigServiceUrl("http://localhost:" + server.getPort()); + } +} diff --git a/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java index c1e5a449109..27e4beb76c7 100644 --- a/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java @@ -45,6 +45,11 @@ import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; +import static org.apache.dubbo.common.constants.CommonConstants.TOKEN; +import static org.apache.dubbo.common.constants.ConsulConstants.DEFAULT_WATCH_TIMEOUT; +import static org.apache.dubbo.common.constants.ConsulConstants.WATCH_TIMEOUT; +import static org.apache.dubbo.common.constants.ConsulConstants.DEFAULT_PORT; +import static org.apache.dubbo.common.constants.ConsulConstants.INVALID_PORT; /** * config center implementation for consul @@ -52,10 +57,6 @@ public class ConsulDynamicConfiguration extends TreePathDynamicConfiguration { private static final Logger logger = LoggerFactory.getLogger(ConsulDynamicConfiguration.class); - private static final int DEFAULT_PORT = 8500; - private static final int DEFAULT_WATCH_TIMEOUT = 60 * 1000; - private static final String WATCH_TIMEOUT = "consul-watch-timeout"; - private final Consul client; private final KeyValueClient kvClient; @@ -68,10 +69,10 @@ public ConsulDynamicConfiguration(URL url) { super(url); watchTimeout = url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT); String host = url.getHost(); - int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT; + int port = INVALID_PORT != url.getPort() ? url.getPort() : DEFAULT_PORT; Consul.Builder builder = Consul.builder() .withHostAndPort(HostAndPort.fromParts(host, port)); - String token = url.getParameter("token", (String) null); + String token = url.getParameter(TOKEN, (String) null); if (StringUtils.isNotEmpty(token)) { builder.withAclToken(token); } diff --git a/dubbo-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java index c54d1034143..60112b9d61b 100644 --- a/dubbo-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java +++ b/dubbo-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java @@ -1,123 +1,123 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.configcenter.consul; - -import org.apache.dubbo.common.URL; - -import com.google.common.net.HostAndPort; -import com.orbitz.consul.Consul; -import com.orbitz.consul.KeyValueClient; -import com.orbitz.consul.cache.KVCache; -import com.orbitz.consul.model.kv.Value; -import com.pszymczyk.consul.ConsulProcess; -import com.pszymczyk.consul.ConsulStarterBuilder; -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 java.util.Arrays; -import java.util.Optional; -import java.util.TreeSet; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * - */ -public class ConsulDynamicConfigurationTest { - - private static ConsulProcess consul; - private static URL configCenterUrl; - private static ConsulDynamicConfiguration configuration; - - private static Consul client; - private static KeyValueClient kvClient; - - @BeforeAll - public static void setUp() throws Exception { - consul = ConsulStarterBuilder.consulStarter() - .build() - .start(); - configCenterUrl = URL.valueOf("consul://127.0.0.1:" + consul.getHttpPort()); - - configuration = new ConsulDynamicConfiguration(configCenterUrl); - client = Consul.builder().withHostAndPort(HostAndPort.fromParts("127.0.0.1", consul.getHttpPort())).build(); - kvClient = client.keyValueClient(); - } - - @AfterAll - public static void tearDown() throws Exception { - consul.close(); - configuration.close(); - } - - @Test - public void testGetConfig() { - kvClient.putValue("/dubbo/config/dubbo/foo", "bar"); - // test equals - assertEquals("bar", configuration.getConfig("foo", "dubbo")); - // test does not block - assertEquals("bar", configuration.getConfig("foo", "dubbo")); - Assertions.assertNull(configuration.getConfig("not-exist", "dubbo")); - } - - @Test - public void testPublishConfig() { - configuration.publishConfig("value", "metadata", "1"); - // test equals - assertEquals("1", configuration.getConfig("value", "/metadata")); - assertEquals("1", kvClient.getValueAsString("/dubbo/config/metadata/value").get()); - } - - @Test - public void testAddListener() { - KVCache cache = KVCache.newCache(kvClient, "/dubbo/config/dubbo/foo"); - cache.addListener(newValues -> { - // Cache notifies all paths with "foo" the root path - // If you want to watch only "foo" value, you must filter other paths - Optional newValue = newValues.values().stream() - .filter(value -> value.getKey().equals("foo")) - .findAny(); - - newValue.ifPresent(value -> { - // Values are encoded in key/value store, decode it if needed - Optional decodedValue = newValue.get().getValueAsString(); - decodedValue.ifPresent(v -> System.out.println(String.format("Value is: %s", v))); //prints "bar" - }); - }); - cache.start(); - - kvClient.putValue("/dubbo/config/dubbo/foo", "new-value"); - kvClient.putValue("/dubbo/config/dubbo/foo/sub", "sub-value"); - kvClient.putValue("/dubbo/config/dubbo/foo/sub2", "sub-value2"); - kvClient.putValue("/dubbo/config/foo", "parent-value"); - - System.out.println(kvClient.getKeys("/dubbo/config/dubbo/foo")); - System.out.println(kvClient.getKeys("/dubbo/config")); - System.out.println(kvClient.getValues("/dubbo/config/dubbo/foo")); - } - - @Test - public void testGetConfigKeys() { - configuration.publishConfig("v1", "metadata", "1"); - configuration.publishConfig("v2", "metadata", "2"); - configuration.publishConfig("v3", "metadata", "3"); - // test equals - assertEquals(new TreeSet(Arrays.asList("v1", "v2", "v3")), configuration.getConfigKeys("metadata")); - } -} +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package org.apache.dubbo.configcenter.consul; +// +//import org.apache.dubbo.common.URL; +// +//import com.google.common.net.HostAndPort; +//import com.orbitz.consul.Consul; +//import com.orbitz.consul.KeyValueClient; +//import com.orbitz.consul.cache.KVCache; +//import com.orbitz.consul.model.kv.Value; +//import com.pszymczyk.consul.ConsulProcess; +//import com.pszymczyk.consul.ConsulStarterBuilder; +//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 java.util.Arrays; +//import java.util.Optional; +//import java.util.TreeSet; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +// +///** +// * +// */ +//public class ConsulDynamicConfigurationTest { +// +// private static ConsulProcess consul; +// private static URL configCenterUrl; +// private static ConsulDynamicConfiguration configuration; +// +// private static Consul client; +// private static KeyValueClient kvClient; +// +// @BeforeAll +// public static void setUp() throws Exception { +// consul = ConsulStarterBuilder.consulStarter() +// .build() +// .start(); +// configCenterUrl = URL.valueOf("consul://127.0.0.1:" + consul.getHttpPort()); +// +// configuration = new ConsulDynamicConfiguration(configCenterUrl); +// client = Consul.builder().withHostAndPort(HostAndPort.fromParts("127.0.0.1", consul.getHttpPort())).build(); +// kvClient = client.keyValueClient(); +// } +// +// @AfterAll +// public static void tearDown() throws Exception { +// consul.close(); +// configuration.close(); +// } +// +// @Test +// public void testGetConfig() { +// kvClient.putValue("/dubbo/config/dubbo/foo", "bar"); +// // test equals +// assertEquals("bar", configuration.getConfig("foo", "dubbo")); +// // test does not block +// assertEquals("bar", configuration.getConfig("foo", "dubbo")); +// Assertions.assertNull(configuration.getConfig("not-exist", "dubbo")); +// } +// +// @Test +// public void testPublishConfig() { +// configuration.publishConfig("value", "metadata", "1"); +// // test equals +// assertEquals("1", configuration.getConfig("value", "/metadata")); +// assertEquals("1", kvClient.getValueAsString("/dubbo/config/metadata/value").get()); +// } +// +// @Test +// public void testAddListener() { +// KVCache cache = KVCache.newCache(kvClient, "/dubbo/config/dubbo/foo"); +// cache.addListener(newValues -> { +// // Cache notifies all paths with "foo" the root path +// // If you want to watch only "foo" value, you must filter other paths +// Optional newValue = newValues.values().stream() +// .filter(value -> value.getKey().equals("foo")) +// .findAny(); +// +// newValue.ifPresent(value -> { +// // Values are encoded in key/value store, decode it if needed +// Optional decodedValue = newValue.get().getValueAsString(); +// decodedValue.ifPresent(v -> System.out.println(String.format("Value is: %s", v))); //prints "bar" +// }); +// }); +// cache.start(); +// +// kvClient.putValue("/dubbo/config/dubbo/foo", "new-value"); +// kvClient.putValue("/dubbo/config/dubbo/foo/sub", "sub-value"); +// kvClient.putValue("/dubbo/config/dubbo/foo/sub2", "sub-value2"); +// kvClient.putValue("/dubbo/config/foo", "parent-value"); +// +// System.out.println(kvClient.getKeys("/dubbo/config/dubbo/foo")); +// System.out.println(kvClient.getKeys("/dubbo/config")); +// System.out.println(kvClient.getValues("/dubbo/config/dubbo/foo")); +// } +// +// @Test +// public void testGetConfigKeys() { +// configuration.publishConfig("v1", "metadata", "1"); +// configuration.publishConfig("v2", "metadata", "2"); +// configuration.publishConfig("v3", "metadata", "3"); +// // test equals +// assertEquals(new TreeSet(Arrays.asList("v1", "v2", "v3")), configuration.getConfigKeys("metadata")); +// } +//} diff --git a/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java index f686e860331..59b384b6c38 100644 --- a/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java @@ -40,7 +40,7 @@ import java.util.concurrent.ConcurrentMap; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; /** diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosConfigServiceWrapper.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosConfigServiceWrapper.java new file mode 100644 index 00000000000..b085597e202 --- /dev/null +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosConfigServiceWrapper.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.configcenter.support.nacos; + +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import com.alibaba.nacos.api.exception.NacosException; + +public class NacosConfigServiceWrapper { + + private static final String INNERCLASS_SYMBOL = "$"; + + private static final String INNERCLASS_COMPATIBLE_SYMBOL = "___"; + + private ConfigService configService; + + + public NacosConfigServiceWrapper(ConfigService configService) { + this.configService = configService; + } + + public ConfigService getConfigService() { + return configService; + } + + public void addListener(String dataId, String group, Listener listener) throws NacosException { + configService.addListener(handleInnerSymbol(dataId), handleInnerSymbol(group), listener); + } + + public String getConfig(String dataId, String group, long timeout) throws NacosException { + return configService.getConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), timeout); + } + + public boolean publishConfig(String dataId, String group, String content) throws NacosException { + return configService.publishConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), content); + } + + public boolean removeConfig(String dataId, String group) throws NacosException { + return configService.removeConfig(handleInnerSymbol(dataId), handleInnerSymbol(group)); + } + + /** + * see {@link com.alibaba.nacos.client.config.utils.ParamUtils#isValid(java.lang.String)} + */ + private String handleInnerSymbol(String dataId) { + if (dataId == null) { + return null; + } + return dataId.replace(INNERCLASS_SYMBOL, INNERCLASS_COMPATIBLE_SYMBOL); + } +} diff --git a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java index cca467f7bf2..310310f3cb3 100644 --- a/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java @@ -24,6 +24,7 @@ import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import com.alibaba.fastjson.JSON; @@ -81,7 +82,7 @@ public class NacosDynamicConfiguration implements DynamicConfiguration { /** * The nacos configService */ - private final ConfigService configService; + private final NacosConfigServiceWrapper configService; private HttpAgent httpAgent; @@ -93,11 +94,11 @@ public class NacosDynamicConfiguration implements DynamicConfiguration { NacosDynamicConfiguration(URL url) { this.nacosProperties = buildNacosProperties(url); this.configService = buildConfigService(url); - this.httpAgent = getHttpAgent(configService); + this.httpAgent = getHttpAgent(configService.getConfigService()); watchListenerMap = new ConcurrentHashMap<>(); } - private ConfigService buildConfigService(URL url) { + private NacosConfigServiceWrapper buildConfigService(URL url) { ConfigService configService = null; try { configService = NacosFactory.createConfigService(nacosProperties); @@ -107,14 +108,14 @@ private ConfigService buildConfigService(URL url) { } throw new IllegalStateException(e); } - return configService; + return new NacosConfigServiceWrapper(configService); } private HttpAgent getHttpAgent(ConfigService configService) { HttpAgent agent = null; try { Field field = configService.getClass().getDeclaredField("agent"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); agent = (HttpAgent) field.get(configService); } catch (Exception e) { throw new IllegalStateException(e); diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java index a0acd51ef4d..ea63ab5ddce 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java @@ -28,7 +28,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.CountDownLatch; import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR; import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR; @@ -38,29 +37,30 @@ */ public class CacheListener implements DataListener { - private static final int MIN_PATH_DEPTH = 5; private Map> keyListeners = new ConcurrentHashMap<>(); - private CountDownLatch initializedLatch; private String rootPath; - public CacheListener(String rootPath, CountDownLatch initializedLatch) { + public CacheListener(String rootPath) { this.rootPath = rootPath; - this.initializedLatch = initializedLatch; } public void addListener(String key, ConfigurationListener configurationListener) { - Set listeners = this.keyListeners.computeIfAbsent(key, k -> new CopyOnWriteArraySet<>()); + Set listeners = keyListeners.computeIfAbsent(key, k -> new CopyOnWriteArraySet<>()); listeners.add(configurationListener); } public void removeListener(String key, ConfigurationListener configurationListener) { - Set listeners = this.keyListeners.get(key); + Set listeners = keyListeners.get(key); if (listeners != null) { listeners.remove(configurationListener); } } + public Set getConfigurationListeners(String key) { + return keyListeners.get(key); + } + /** * This is used to convert a configuration nodePath into a key * TODO doc @@ -92,43 +92,19 @@ private String getGroup(String path) { @Override public void dataChanged(String path, Object value, EventType eventType) { - if (eventType == null) { - return; - } - - if (eventType == EventType.INITIALIZED) { - initializedLatch.countDown(); - return; - } - - if (path == null || (value == null && eventType != EventType.NodeDeleted)) { - return; + ConfigChangeType changeType; + if (value == null) { + changeType = ConfigChangeType.DELETED; + } else { + changeType = ConfigChangeType.MODIFIED; } + String key = pathToKey(path); - // TODO We only care the changes happened on a specific path level, for example - // /dubbo/config/dubbo/configurators, other config changes not in this level will be ignored, - if (path.split("/").length >= MIN_PATH_DEPTH) { - String key = pathToKey(path); - ConfigChangeType changeType; - switch (eventType) { - case NodeCreated: - changeType = ConfigChangeType.ADDED; - break; - case NodeDeleted: - changeType = ConfigChangeType.DELETED; - break; - case NodeDataChanged: - changeType = ConfigChangeType.MODIFIED; - break; - default: - return; - } - - ConfigChangedEvent configChangeEvent = new ConfigChangedEvent(key, getGroup(path), (String) value, changeType); - Set listeners = keyListeners.get(path); - if (CollectionUtils.isNotEmpty(listeners)) { - listeners.forEach(listener -> listener.process(configChangeEvent)); - } + ConfigChangedEvent configChangeEvent = new ConfigChangedEvent(key, getGroup(path), (String) value, changeType); + Set listeners = keyListeners.get(path); + if (CollectionUtils.isNotEmpty(listeners)) { + listeners.forEach(listener -> listener.process(configChangeEvent)); } } } + diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java index a96f8438f16..3b80455ed9b 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java @@ -19,15 +19,15 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.configcenter.ConfigurationListener; import org.apache.dubbo.common.config.configcenter.TreePathDynamicConfiguration; +import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.remoting.zookeeper.ZookeeperClient; import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter; import java.util.Collection; -import java.util.concurrent.CountDownLatch; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; /** * @@ -38,7 +38,6 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration // The final root path would be: /configRootPath/"config" private String rootPath; private final ZookeeperClient zkClient; - private CountDownLatch initializedLatch; private CacheListener cacheListener; private URL url; @@ -49,22 +48,12 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration this.url = url; rootPath = getRootPath(url); - initializedLatch = new CountDownLatch(1); - this.cacheListener = new CacheListener(rootPath, initializedLatch); + this.cacheListener = new CacheListener(rootPath); this.executor = Executors.newFixedThreadPool(1, new NamedThreadFactory(this.getClass().getSimpleName(), true)); - zkClient = zookeeperTransporter.connect(url); - zkClient.addDataListener(rootPath, cacheListener, executor); - try { - // Wait for connection - long timeout = url.getParameter("init.timeout", 5000); - boolean isCountDown = this.initializedLatch.await(timeout, TimeUnit.MILLISECONDS); - if (!isCountDown) { - throw new IllegalStateException("Failed to receive INITIALIZED event from zookeeper, pls. check if url " - + url + " is correct"); - } - } catch (InterruptedException e) { - logger.warn("Failed to build local cache for config center (zookeeper)." + url); + boolean isConnected = zkClient.isConnected(); + if (!isConnected) { + throw new IllegalStateException("Failed to connect with zookeeper, pls check if url " + url + " is correct."); } } @@ -74,7 +63,7 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration */ @Override public String getInternalProperty(String key) { - return zkClient.getContent(key); + return zkClient.getContent(buildPathKey("", key)); } @Override @@ -107,10 +96,15 @@ protected Collection doGetConfigKeys(String groupPath) { @Override protected void doAddListener(String pathKey, ConfigurationListener listener) { cacheListener.addListener(pathKey, listener); + zkClient.addDataListener(pathKey, cacheListener, executor); } @Override protected void doRemoveListener(String pathKey, ConfigurationListener listener) { cacheListener.removeListener(pathKey, listener); + Set configurationListeners = cacheListener.getConfigurationListeners(pathKey); + if (CollectionUtils.isNotEmpty(configurationListeners)) { + zkClient.removeDataListener(pathKey, cacheListener); + } } } diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java index 9d4a0c36d3f..ddac80a6b3e 100644 --- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java +++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java @@ -65,7 +65,7 @@ public static void setUp() throws Exception { try { setData("/dubbo/config/dubbo/dubbo.properties", "The content from dubbo.properties"); setData("/dubbo/config/dubbo/service:version:group.configurators", "The content from configurators"); - setData("/dubbo/config/appname", "The content from higer level node"); + setData("/dubbo/config/appname", "The content from higher level node"); setData("/dubbo/config/dubbo/appname.tag-router", "The content from appname tagrouters"); setData("/dubbo/config/dubbo/never.change.DemoService.configurators", "Never change value from configurators"); } catch (Exception e) { @@ -107,6 +107,7 @@ public void testAddListener() throws Exception { configuration.addListener("appname.tag-router", listener3); configuration.addListener("appname.tag-router", listener4); + Thread.sleep(100); setData("/dubbo/config/dubbo/service:version:group.configurators", "new value1"); Thread.sleep(100); setData("/dubbo/config/dubbo/appname.tag-router", "new value2"); @@ -116,10 +117,10 @@ public void testAddListener() throws Exception { Thread.sleep(5000); latch.await(); - Assertions.assertEquals(1, listener1.getCount("service:version:group.configurators")); - Assertions.assertEquals(1, listener2.getCount("service:version:group.configurators")); - Assertions.assertEquals(1, listener3.getCount("appname.tag-router")); - Assertions.assertEquals(1, listener4.getCount("appname.tag-router")); + Assertions.assertEquals(2, listener1.getCount("service:version:group.configurators")); + Assertions.assertEquals(2, listener2.getCount("service:version:group.configurators")); + Assertions.assertEquals(2, listener3.getCount("appname.tag-router")); + Assertions.assertEquals(2, listener4.getCount("appname.tag-router")); Assertions.assertEquals("new value1", listener1.getValue()); Assertions.assertEquals("new value1", listener2.getValue()); diff --git a/dubbo-container/dubbo-container-api/src/main/java/org/apache/dubbo/container/Main.java b/dubbo-container/dubbo-container-api/src/main/java/org/apache/dubbo/container/Main.java index 91c76efe06e..0ad5be10349 100644 --- a/dubbo-container/dubbo-container-api/src/main/java/org/apache/dubbo/container/Main.java +++ b/dubbo-container/dubbo-container-api/src/main/java/org/apache/dubbo/container/Main.java @@ -62,7 +62,7 @@ public static void main(String[] args) { for (int i = 0; i < args.length; i++) { containers.add(LOADER.getExtension(args[i])); } - logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce."); + logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo service."); if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) { Runtime.getRuntime().addShutdownHook(new Thread("dubbo-container-shutdown-hook") { diff --git a/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh index 50c3a708e65..b87ac79e8fd 100755 --- a/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh +++ b/dubbo-container/dubbo-container-api/src/main/resources/META-INF/assembly/bin/start.sh @@ -21,11 +21,11 @@ cd .. DEPLOY_DIR=`pwd` CONF_DIR=$DEPLOY_DIR/conf -SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -SERVER_PROTOCOL=`sed '/dubbo.protocol.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -SERVER_HOST=`sed '/dubbo.protocol.host/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -SERVER_PORT=`sed '/dubbo.protocol.port/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` -LOGS_FILE=`sed '/dubbo.log4j.file/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_NAME=`sed '/^dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_PROTOCOL=`sed '/^dubbo.protocol.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_HOST=`sed '/^dubbo.protocol.host/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +SERVER_PORT=`sed '/^dubbo.protocol.port/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` +LOGS_FILE=`sed '/^dubbo.log4j.file/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'` VM_ARGS_PERM_SIZE='PermSize' VM_ARGS_METASPACE_SIZE='MetaspaceSize' JAVA_8_VERSION="180" diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml index b3a4accb3f3..1e5a7d51b39 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml @@ -34,6 +34,10 @@ org.apache.dubbo dubbo-metadata-report-zookeeper + + org.apache.dubbo + dubbo-metadata-report-failover + org.apache.dubbo dubbo-demo-interface @@ -83,5 +87,16 @@ org.apache.dubbo dubbo-serialization-hessian2 + + org.apache.dubbo + dubbo-serialization-jdk + + + + org.codehaus.jackson + jackson-core-asl + 1.9.12 + + diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml index 9225959fcd6..9c632b89f1e 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml @@ -1,39 +1,39 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml index c4590c2671b..438c5429d38 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml @@ -70,6 +70,10 @@ org.apache.dubbo dubbo-metadata-report-zookeeper + + org.apache.dubbo + dubbo-metadata-report-failover + org.apache.dubbo dubbo-rpc-dubbo @@ -86,6 +90,10 @@ org.apache.dubbo dubbo-serialization-hessian2 + + org.apache.dubbo + dubbo-serialization-jdk + org.apache.dubbo dubbo-qos @@ -103,5 +111,13 @@ log4j log4j + + + + org.codehaus.jackson + jackson-core-asl + 1.9.12 + + diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml index 856ff735fc2..05d6f8f684a 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml @@ -21,13 +21,13 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> - + - + diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index e304361b861..2833015a2f7 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -90,7 +90,7 @@ 5.2.8.RELEASE - 3.20.0-GA + 3.23.1-GA 3.2.5.Final 4.1.51.Final 1.1.7 @@ -149,15 +149,14 @@ 4.10.3 - 1.0.8 + 1.0.10 2.2.7 1.2.0 - 1.11.2 + 1.15.2 0.5.3 3.2.8 1.5.19 - 4.3.16.RELEASE 2.0.1 5.2.0 @@ -166,7 +165,7 @@ 6.1.26 2.0 1.1.0 - 2.7.9-SNAPSHOT + 2.7.11-SNAPSHOT @@ -630,7 +629,7 @@ org.springframework spring-test - ${spring_test_version} + ${spring_version} test diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml index e5c54c80beb..081290e243e 100644 --- a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml +++ b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml @@ -32,7 +32,7 @@ pom - 2.7.9-SNAPSHOT + 2.7.11-SNAPSHOT 1.1.0 @@ -51,7 +51,7 @@ org.apache.curator - curator-recipes + curator-x-discovery org.apache.zookeeper diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java index 902bf6dceab..ebd6f95490c 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java +++ b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java @@ -37,7 +37,7 @@ /** * CacheFilter is a core component of dubbo.Enabling cache key of service,method,consumer or provider dubbo will cache method return value. * Along with cache key we need to configure cache type. Dubbo default implemented cache types are - *
  • lur
  • + *
  • lru
  • *
  • threadlocal
  • *
  • jcache
  • *
  • expiring
  • diff --git a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java index 895f11408c0..b5306e22437 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java +++ b/dubbo-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java @@ -84,6 +84,12 @@ public V put(K key, V value) { public V get(Object key) { ExpiryObject object = delegateMap.get(key); if (object != null) { + long timeIdle = System.currentTimeMillis() - object.getLastAccessTime(); + int timeToLive = expireThread.getTimeToLive(); + if (timeToLive > 0 && timeIdle >= timeToLive * 1000) { + delegateMap.remove(object.getKey()); + return null; + } object.setLastAccessTime(System.currentTimeMillis()); return object.getValue(); } @@ -137,6 +143,9 @@ public Set keySet() { @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } return delegateMap.equals(obj); } @@ -226,6 +235,9 @@ public V getValue() { @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } return value.equals(obj); } @@ -282,10 +294,10 @@ public void run() { private void processExpires() { long timeNow = System.currentTimeMillis(); + if (timeToLiveMillis <= 0) { + return; + } for (ExpiryObject o : delegateMap.values()) { - if (timeToLiveMillis <= 0) { - continue; - } long timeIdle = timeNow - o.getLastAccessTime(); if (timeIdle >= timeToLiveMillis) { delegateMap.remove(o.getKey()); @@ -307,7 +319,7 @@ public void startExpiring() { * start thread */ public void startExpiryIfNotStarted() { - if (running) { + if (running && timeToLiveMillis <= 0) { return; } startExpiring(); diff --git a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory index c849461da19..54a1dbf7b2f 100644 --- a/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory +++ b/dubbo-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory @@ -1,4 +1,5 @@ threadlocal=org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory lru=org.apache.dubbo.cache.support.lru.LruCacheFactory jcache=org.apache.dubbo.cache.support.jcache.JCacheFactory -expiring=org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory \ No newline at end of file +expiring=org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory +lfu=org.apache.dubbo.cache.support.lfu.LfuCacheFactory diff --git a/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java b/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java index 23484c6c327..877faf4970c 100644 --- a/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java +++ b/dubbo-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java @@ -19,18 +19,45 @@ import org.apache.dubbo.cache.Cache; import org.apache.dubbo.cache.support.AbstractCacheFactory; import org.apache.dubbo.cache.support.AbstractCacheFactoryTest; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.RpcInvocation; import org.junit.jupiter.api.Test; -import static org.hamcrest.core.Is.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class ExpiringCacheFactoryTest extends AbstractCacheFactoryTest { @Test - public void testLruCacheFactory() throws Exception { + public void testExpiringCacheFactory() throws Exception { Cache cache = super.constructCache(); assertThat(cache instanceof ExpiringCache, is(true)); } + @Test + public void testExpiringCacheGetExpired() throws Exception { + URL url = URL.valueOf("test://test:12/test?cache=expiring&cache.seconds=1&cache.interval=1"); + AbstractCacheFactory cacheFactory = getCacheFactory(); + Invocation invocation = new RpcInvocation(); + Cache cache = cacheFactory.getCache(url, invocation); + cache.put("testKey", "testValue"); + Thread.sleep(2100); + assertNull(cache.get("testKey")); + } + + @Test + public void testExpiringCacheUnExpired() throws Exception { + URL url = URL.valueOf("test://test:12/test?cache=expiring&cache.seconds=0&cache.interval=1"); + AbstractCacheFactory cacheFactory = getCacheFactory(); + Invocation invocation = new RpcInvocation(); + Cache cache = cacheFactory.getCache(url, invocation); + cache.put("testKey", "testValue"); + Thread.sleep(1100); + assertNotNull(cache.get("testKey")); + } + @Override protected AbstractCacheFactory getCacheFactory() { return new ExpiringCacheFactory(); diff --git a/dubbo-metadata/dubbo-metadata-api/pom.xml b/dubbo-metadata/dubbo-metadata-api/pom.xml index fe6a20c1903..9caf31e4323 100644 --- a/dubbo-metadata/dubbo-metadata-api/pom.xml +++ b/dubbo-metadata/dubbo-metadata-api/pom.xml @@ -78,6 +78,13 @@ test
    + + + org.springframework + spring-context + test + +
    \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java index 12d6665ce9d..49f953988ab 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.CollectionUtils; import java.util.Collections; import java.util.LinkedHashSet; @@ -66,7 +67,7 @@ public void map(URL url) { dynamicConfiguration.publishConfig(key, ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), content); if (logger.isInfoEnabled()) { logger.info(String.format("Dubbo service[%s] mapped to interface name[%s].", - group, serviceInterface, group)); + group, serviceInterface)); } }); } @@ -83,7 +84,9 @@ public Set getAndListen(URL url, MappingListener mappingListener) { execute(() -> { Set keys = dynamicConfiguration .getConfigKeys(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol)); - serviceNames.addAll(keys); + if (CollectionUtils.isNotEmpty(keys)) { + serviceNames.addAll(keys); + } }); return Collections.unmodifiableSet(serviceNames); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java index 6bf38c9aac8..7516b37fd43 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java @@ -19,7 +19,7 @@ public class MetadataConstants { public static final String KEY_SEPARATOR = ":"; public static final String DEFAULT_PATH_TAG = "metadata"; - public static final String KEY_REVISON_PREFIX = "revision"; + public static final String KEY_REVISION_PREFIX = "revision"; public static final String META_DATA_STORE_TAG = ".metaData"; public static final String SERVICE_META_DATA_STORE_TAG = ".smd"; public static final String CONSUMER_META_DATA_STORE_TAG = ".cmd"; diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java index 6fc62391efc..915b2cd45f1 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java @@ -178,7 +178,7 @@ public static class ServiceInfo implements Serializable { private String path; // most of the time, path is the same with the interface name. private Map params; - // params configuried on consumer side, + // params configured on consumer side, private transient Map consumerParams; // cached method params private transient Map> methodParams; diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java index 4971d72281a..d8a864d4b73 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java @@ -32,7 +32,7 @@ import static org.apache.dubbo.common.URL.buildKey; /** - * A framework interface of Dubbo Metadata Service defines the contract of Dubbo Services registartion and subscription + * A framework interface of Dubbo Metadata Service defines the contract of Dubbo Services registration and subscription * between Dubbo service providers and its consumers. The implementation will be exported as a normal Dubbo service that * the clients would subscribe, whose version comes from the {@link #version()} method and group gets from * {@link #serviceName()}, that means, The different Dubbo service(application) will export the different diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java index 4fd38653598..f146731ba4c 100755 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/builder/MapTypeBuilder.java @@ -25,7 +25,6 @@ import java.util.Arrays; import java.util.Map; -import static org.apache.dubbo.common.utils.StringUtils.replace; import static org.apache.dubbo.common.utils.TypeUtils.getRawClass; import static org.apache.dubbo.common.utils.TypeUtils.isClass; import static org.apache.dubbo.common.utils.TypeUtils.isParameterizedType; @@ -59,13 +58,7 @@ public TypeDefinition build(Type type, Class clazz, Map, TypeDefinit + Arrays.toString(actualTypeArgs), type, actualTypeArgs)); } - // Change since 2.7.6 - /** - * Replacing ", " to "," will not change the semantic of - * {@link ParameterizedType#toString()} - * @see sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl - */ - String mapType = replace(type.toString(), ", ", ","); + String mapType = type.toString(); TypeDefinition typeDefinition = new TypeDefinition(mapType); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java index 36356216c8b..c51d42e3e9f 100755 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java @@ -22,6 +22,9 @@ import java.util.List; import java.util.Objects; +import static org.apache.dubbo.metadata.definition.model.TypeDefinition.formatType; +import static org.apache.dubbo.metadata.definition.model.TypeDefinition.formatTypes; + /** * 2015/1/27. */ @@ -60,11 +63,11 @@ public void setParameters(List parameters) { } public void setParameterTypes(String[] parameterTypes) { - this.parameterTypes = parameterTypes; + this.parameterTypes = formatTypes(parameterTypes); } public void setReturnType(String returnType) { - this.returnType = returnType; + this.returnType = formatType(returnType); } @Override diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java index c5e72438038..57214139668 100755 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/TypeDefinition.java @@ -19,12 +19,15 @@ import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import static org.apache.dubbo.common.utils.StringUtils.replace; + /** * 2015/1/27. */ @@ -44,7 +47,52 @@ public TypeDefinition() { } public TypeDefinition(String type) { - this.type = type; + this.setType(type); + } + + /** + * Format the {@link String} array presenting Java types + * + * @param types the strings presenting Java types + * @return new String array of Java types after be formatted + * @since 2.7.9 + */ + public static String[] formatTypes(String[] types) { + String[] newTypes = new String[types.length]; + for (int i = 0; i < types.length; i++) { + newTypes[i] = formatType(types[i]); + } + return newTypes; + } + + /** + * Format the {@link String} presenting Java type + * + * @param type the String presenting type + * @return new String presenting Java type after be formatted + * @since 2.7.9 + */ + public static String formatType(String type) { + if (isGenericType(type)) { + return formatGenericType(type); + } + return type; + } + + /** + * Replacing ", " to "," will not change the semantic of + * {@link ParameterizedType#toString()} + * + * @param type + * @return formatted type + * @see sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl + */ + private static String formatGenericType(String type) { + return replace(type, ", ", ","); + } + + private static boolean isGenericType(String type) { + return type.contains("<") && type.contains(">"); } public String get$ref() { @@ -105,7 +153,7 @@ public void setProperties(Map properties) { } public void setType(String type) { - this.type = type; + this.type = formatType(type); } public void setTypeBuilderName(String typeBuilderName) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java index 8d11784f69d..c8f1d59be57 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java @@ -20,11 +20,13 @@ import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.config.MetadataReportConfig; +import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_DIRECTORY; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.metadata.report.support.Constants.METADATA_REPORT_KEY; @@ -51,6 +53,7 @@ public static void init(MetadataReportConfig config) { .removeParameter(METADATA_REPORT_KEY) .build(); } + url = url.addParameterIfAbsent(APPLICATION_KEY, ApplicationModel.getApplicationConfig().getName()); String relatedRegistryId = config.getRegistry() == null ? DEFAULT_KEY : config.getRegistry(); // RegistryConfig registryConfig = ApplicationModel.getConfigManager().getRegistry(relatedRegistryId) // .orElseThrow(() -> new IllegalStateException("Registry id " + relatedRegistryId + " does not exist.")); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/ServiceMetadataIdentifier.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/ServiceMetadataIdentifier.java index 01a2196b09c..66a11462e77 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/ServiceMetadataIdentifier.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/ServiceMetadataIdentifier.java @@ -21,7 +21,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; -import static org.apache.dubbo.metadata.MetadataConstants.KEY_REVISON_PREFIX; +import static org.apache.dubbo.metadata.MetadataConstants.KEY_REVISION_PREFIX; /** * The ServiceMetadataIdentifier is used to store the {@link org.apache.dubbo.common.URL} @@ -56,11 +56,11 @@ public ServiceMetadataIdentifier(URL url) { } public String getUniqueKey(KeyTypeEnum keyType) { - return super.getUniqueKey(keyType, protocol, KEY_REVISON_PREFIX + revision); + return super.getUniqueKey(keyType, protocol, KEY_REVISION_PREFIX + revision); } public String getIdentifierKey() { - return super.getIdentifierKey(protocol, KEY_REVISON_PREFIX + revision); + return super.getIdentifierKey(protocol, KEY_REVISION_PREFIX + revision); } public void setRevision(String revision) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java index ea074214ec4..eb48bdaa21e 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/AbstractServiceRestMetadataResolver.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.utils.MethodComparator; import org.apache.dubbo.common.utils.ServiceAnnotationResolver; +import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.Service; import org.apache.dubbo.metadata.definition.MethodDefinitionBuilder; import org.apache.dubbo.metadata.definition.model.MethodDefinition; @@ -70,7 +71,8 @@ protected final boolean isImplementedInterface(Class serviceType) { } protected final boolean isServiceAnnotationPresent(Class serviceType) { - return isAnyAnnotationPresent(serviceType, Service.class, com.alibaba.dubbo.config.annotation.Service.class); + return isAnyAnnotationPresent(serviceType, DubboService.class, Service.class, + com.alibaba.dubbo.config.annotation.Service.class); } /** @@ -296,7 +298,7 @@ private void processAnnotatedMethodParameters(Method serviceMethod, Class ser for (int i = 0; i < paramCount; i++) { Parameter parameter = parameters[i]; // Add indexed parameter name - metadata.addIndexToName(i,parameter.getName()); + metadata.addIndexToName(i, parameter.getName()); processAnnotatedMethodParameter(parameter, i, serviceMethod, serviceType, serviceInterfaceClass, metadata); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java index bd4d5a0a733..f8b9514fa80 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ClassPathServiceRestMetadataReader.java @@ -41,14 +41,14 @@ */ public class ClassPathServiceRestMetadataReader implements ServiceRestMetadataReader { - private final String serviceRestMetadataJsonResoucePath; + private final String serviceRestMetadataJsonResourcePath; public ClassPathServiceRestMetadataReader() { this(SERVICE_REST_METADATA_RESOURCE_PATH); } - public ClassPathServiceRestMetadataReader(String serviceRestMetadataJsonResoucePath) { - this.serviceRestMetadataJsonResoucePath = serviceRestMetadataJsonResoucePath; + public ClassPathServiceRestMetadataReader(String serviceRestMetadataJsonResourcePath) { + this.serviceRestMetadataJsonResourcePath = serviceRestMetadataJsonResourcePath; } @Override @@ -59,7 +59,7 @@ public List read() { ClassLoader classLoader = getClass().getClassLoader(); execute(() -> { - Enumeration resources = classLoader.getResources(serviceRestMetadataJsonResoucePath); + Enumeration resources = classLoader.getResources(serviceRestMetadataJsonResourcePath); Gson gson = new Gson(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java index 82e6328574a..6c758bf23b9 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java @@ -16,6 +16,11 @@ */ package org.apache.dubbo.metadata.rest; +import java.lang.annotation.Annotation; + +import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader; +import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; + /** * The REST Metadata Constants definition interface * @@ -108,5 +113,33 @@ interface SPRING_MVC { * The annotation class name of @RequestParam */ String REQUEST_PARAM_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestParam"; + + /** + * The class of @Controller + * + * @since 2.7.9 + */ + Class CONTROLLER_ANNOTATION_CLASS = (Class) resolveClass(CONTROLLER_ANNOTATION_CLASS_NAME, getClassLoader()); + + /** + * The class of @RequestMapping + * + * @since 2.7.9 + */ + Class REQUEST_MAPPING_ANNOTATION_CLASS = (Class) resolveClass(REQUEST_MAPPING_ANNOTATION_CLASS_NAME, getClassLoader()); + + /** + * The annotation class name of AnnotatedElementUtils + * + * @since 2.7.9 + */ + String ANNOTATED_ELEMENT_UTILS_CLASS_NAME = "org.springframework.core.annotation.AnnotatedElementUtils"; + + /** + * The class of AnnotatedElementUtils + * + * @since 2.7.9 + */ + Class ANNOTATED_ELEMENT_UTILS_CLASS = resolveClass(ANNOTATED_ELEMENT_UTILS_CLASS_NAME, getClassLoader()); } } diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java index a675eb3da02..1531c973fda 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/RestMethodMetadata.java @@ -159,8 +159,12 @@ public void setIndexToEncoded(Map indexToEncoded) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof RestMethodMetadata)) return false; + if (this == o) { + return true; + } + if (!(o instanceof RestMethodMetadata)) { + return false; + } RestMethodMetadata that = (RestMethodMetadata) o; return Objects.equals(getMethod(), that.getMethod()) && Objects.equals(getRequest(), that.getRequest()) && diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java index 876b8a354ac..df396b0d703 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/ServiceRestMetadata.java @@ -76,8 +76,12 @@ public void setMeta(Set meta) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ServiceRestMetadata)) return false; + if (this == o) { + return true; + } + if (!(o instanceof ServiceRestMetadata)) { + return false; + } ServiceRestMetadata that = (ServiceRestMetadata) o; return Objects.equals(getServiceInterface(), that.getServiceInterface()) && Objects.equals(getVersion(), that.getVersion()) && diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/AbstractRequestAnnotationParameterProcessor.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/AbstractRequestAnnotationParameterProcessor.java index 28407c267a8..74807ea280e 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/AbstractRequestAnnotationParameterProcessor.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/AbstractRequestAnnotationParameterProcessor.java @@ -16,11 +16,13 @@ */ package org.apache.dubbo.metadata.rest.springmvc; +import org.apache.dubbo.common.utils.AnnotationUtils; import org.apache.dubbo.metadata.rest.AbstractAnnotatedMethodParameterProcessor; import org.apache.dubbo.metadata.rest.AnnotatedMethodParameterProcessor; import java.lang.annotation.Annotation; import java.lang.reflect.Parameter; +import java.util.Objects; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute; @@ -49,11 +51,18 @@ protected String getAnnotationValue(Annotation annotation, Parameter parameter, @Override protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) { - String defaultValue = getAttribute(annotation, "defaultValue"); - if (isEmpty(defaultValue)) { - defaultValue = super.getDefaultValue(annotation, parameter, parameterIndex); + String attributeName = "defaultValue"; + String attributeValue = getAttribute(annotation, attributeName); + + if (isEmpty(attributeValue) || isDefaultValue(annotation, attributeName, attributeValue)) { + attributeValue = super.getDefaultValue(annotation, parameter, parameterIndex); } - return defaultValue; + return attributeValue; + } + + private boolean isDefaultValue(Annotation annotation, String attributeName, Object attributeValue) { + String defaultValue = AnnotationUtils.getDefaultValue(annotation, attributeName); + return Objects.equals(attributeValue, defaultValue); } protected boolean isEmpty(String str) { diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java index e24422a7465..de64592ab82 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java +++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java @@ -29,14 +29,15 @@ import static java.lang.reflect.Array.getLength; import static java.util.stream.Stream.of; import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation; -import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation; import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute; import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent; import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty; import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty; +import static org.apache.dubbo.common.utils.MethodUtils.findMethod; import static org.apache.dubbo.common.utils.PathUtils.buildPath; -import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS_NAME; -import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS_NAME; +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.ANNOTATED_ELEMENT_UTILS_CLASS; +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS; +import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS; /** * {@link ServiceRestMetadataResolver} @@ -49,23 +50,17 @@ public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMet @Override protected boolean supports0(Class serviceType) { - return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS_NAME); + return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS); } @Override protected boolean isRestCapableMethod(Method serviceMethod, Class serviceType, Class serviceInterfaceClass) { - return isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); + return isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS) || + isAnnotationPresent(serviceMethod, REQUEST_MAPPING_ANNOTATION_CLASS); } @Override protected String resolveRequestMethod(Method serviceMethod, Class serviceType, Class serviceInterfaceClass) { - String requestBasePath = resolveRequestPath(serviceType); - String requestRelativePath = resolveRequestPath(serviceMethod); - return buildPath(requestBasePath, requestRelativePath); - } - - @Override - protected String resolveRequestPath(Method serviceMethod, Class serviceType, Class serviceInterfaceClass) { Annotation requestMapping = getRequestMapping(serviceMethod); // httpMethod is an array of RequestMethod @@ -79,6 +74,13 @@ protected String resolveRequestPath(Method serviceMethod, Class serviceType, return valueOf(Array.get(httpMethod, FIRST_ELEMENT_INDEX)); } + @Override + protected String resolveRequestPath(Method serviceMethod, Class serviceType, Class serviceInterfaceClass) { + String requestBasePath = resolveRequestPath(serviceType); + String requestRelativePath = resolveRequestPath(serviceMethod); + return buildPath(requestBasePath, requestRelativePath); + } + @Override protected void processProduces(Method serviceMethod, Class serviceType, Class serviceInterfaceClass, Set produces) { addMediaTypes(serviceMethod, "produces", produces); @@ -91,6 +93,7 @@ protected void processConsumes(Method serviceMethod, Class serviceType, Class private String resolveRequestPath(AnnotatedElement annotatedElement) { Annotation mappingAnnotation = getRequestMapping(annotatedElement); + // try "value" first String[] value = getAttribute(mappingAnnotation, "value"); @@ -118,10 +121,20 @@ private void addMediaTypes(Method serviceMethod, String annotationAttributeName, private Annotation getRequestMapping(AnnotatedElement annotatedElement) { // try "@RequestMapping" first - Annotation requestMapping = findAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); - // try the annotation meta-annotated later + Annotation requestMapping = findAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS); if (requestMapping == null) { - requestMapping = findMetaAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); + // To try the meta-annotated annotation if can't be found. + // For example, if the annotation "@GetMapping" is used in the Spring Framework is 4.2 or above, + // because of "@GetMapping" alias for ("@AliasFor") "@RequestMapping" , both of them belongs to + // the artifact "spring-web" which depends on "spring-core", thus Spring core's + // AnnotatedElementUtils.findMergedAnnotation(AnnotatedElement, Class) must be involved. + Method method = findMethod(ANNOTATED_ELEMENT_UTILS_CLASS, "findMergedAnnotation", AnnotatedElement.class, Class.class); + if (method != null) { + try { + requestMapping = (Annotation) method.invoke(null, annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS); + } catch (Exception ignored) { + } + } } return requestMapping; } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java index ead518641dd..f967ad7c5f3 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; @@ -35,7 +35,7 @@ * * @since 2.7.6 */ -@Service(version = "2.0.0") +@DubboService(version = "2.0.0", group = "spring") @RestController public class SpringRestService implements RestService { diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java index f5d94b7c1e4..684d76fd3b2 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; @@ -34,7 +34,7 @@ /** * JAX-RS {@link RestService} */ -@Service(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") +@DubboService(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") @Path("/") public class StandardRestService implements RestService { diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/resolver/jaxrs/JAXRSServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java similarity index 96% rename from dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/resolver/jaxrs/JAXRSServiceRestMetadataResolverTest.java rename to dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java index d959007aba4..53c54288676 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/resolver/jaxrs/JAXRSServiceRestMetadataResolverTest.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/jaxrs/JAXRSServiceRestMetadataResolverTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.metadata.rest.resolver.jaxrs; +package org.apache.dubbo.metadata.rest.jaxrs; import org.apache.dubbo.metadata.rest.ClassPathServiceRestMetadataReader; import org.apache.dubbo.metadata.rest.DefaultRestService; @@ -23,7 +23,6 @@ import org.apache.dubbo.metadata.rest.ServiceRestMetadata; import org.apache.dubbo.metadata.rest.SpringRestService; import org.apache.dubbo.metadata.rest.StandardRestService; -import org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver; import org.junit.jupiter.api.Test; diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java new file mode 100644 index 00000000000..43abc8f2a31 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolverTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.rest.springmvc; + +import org.apache.dubbo.metadata.rest.ClassPathServiceRestMetadataReader; +import org.apache.dubbo.metadata.rest.DefaultRestService; +import org.apache.dubbo.metadata.rest.RestMethodMetadata; +import org.apache.dubbo.metadata.rest.RestService; +import org.apache.dubbo.metadata.rest.ServiceRestMetadata; +import org.apache.dubbo.metadata.rest.SpringRestService; +import org.apache.dubbo.metadata.rest.StandardRestService; + +import org.junit.jupiter.api.Test; + +import java.util.LinkedList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link SpringMvcServiceRestMetadataResolver} Test + * + * @since 2.7.9 + */ +public class SpringMvcServiceRestMetadataResolverTest { + + private SpringMvcServiceRestMetadataResolver instance = new SpringMvcServiceRestMetadataResolver(); + + @Test + public void testSupports() { + // Spring MVC RestService class + assertTrue(instance.supports(SpringRestService.class)); + // JAX-RS RestService class + assertFalse(instance.supports(StandardRestService.class)); + // Default RestService class + assertFalse(instance.supports(DefaultRestService.class)); + // No annotated RestService class + assertFalse(instance.supports(RestService.class)); + // null + assertFalse(instance.supports(null)); + } + + @Test + public void testResolve() { + // Generated by "dubbo-metadata-processor" + ClassPathServiceRestMetadataReader reader = new ClassPathServiceRestMetadataReader("META-INF/dubbo/spring-mvc-servoce-rest-metadata.json"); + List serviceRestMetadataList = reader.read(); + + ServiceRestMetadata expectedServiceRestMetadata = serviceRestMetadataList.get(0); + ServiceRestMetadata serviceRestMetadata = instance.resolve(SpringRestService.class); + + + List meta1 = new LinkedList<>(expectedServiceRestMetadata.getMeta()); + List meta2 = new LinkedList<>(serviceRestMetadata.getMeta()); + + for (int i = 0; i < meta1.size(); i++) { + RestMethodMetadata restMethodMetadata = meta1.get(i); + RestMethodMetadata restMethodMetadata2 = meta2.get(i); + assertEquals(restMethodMetadata, restMethodMetadata2); + } + + assertEquals(expectedServiceRestMetadata, serviceRestMetadata); + + } +} diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/test/JTestMetadataReport4Test.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/test/JTestMetadataReport4Test.java index 34ebce39c3d..60f7c7ccfaf 100644 --- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/test/JTestMetadataReport4Test.java +++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/test/JTestMetadataReport4Test.java @@ -17,8 +17,6 @@ package org.apache.dubbo.metadata.test; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; @@ -37,9 +35,6 @@ */ public class JTestMetadataReport4Test extends AbstractMetadataReport { - private final static Logger logger = LoggerFactory.getLogger(JTestMetadataReport4Test.class); - - public JTestMetadataReport4Test(URL url) { super(url); } diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/spring-mvc-servoce-rest-metadata.json b/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/spring-mvc-servoce-rest-metadata.json new file mode 100644 index 00000000000..3d7cbec3758 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-api/src/test/resources/META-INF/dubbo/spring-mvc-servoce-rest-metadata.json @@ -0,0 +1,349 @@ +[ + { + "serviceInterface": "org.apache.dubbo.metadata.rest.RestService", + "version": "2.0.0", + "group": "spring", + "meta": [ + { + "method": { + "name": "form", + "parameterTypes": [ + "java.lang.String" + ], + "returnType": "java.lang.String", + "parameters": [ + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + } + ] + }, + "request": { + "method": "POST", + "path": "/form", + "params": { + "f": [ + "{0}" + ] + }, + "headers": {}, + "consumes": [], + "produces": [] + }, + "indexToName": { + "0": [ + "form" + ] + } + }, + { + "method": { + "name": "headers", + "parameterTypes": [ + "java.lang.String", + "java.lang.String", + "java.lang.Integer" + ], + "returnType": "java.lang.String", + "parameters": [ + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + }, + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + }, + { + "type": "java.lang.Integer", + "items": [], + "enum": [], + "properties": {} + } + ] + }, + "request": { + "method": "GET", + "path": "/headers", + "params": { + "v": [ + "1" + ] + }, + "headers": { + "h": [ + "value-h" + ], + "h2": [ + "value-h2" + ] + }, + "consumes": [], + "produces": [] + }, + "indexToName": { + "0": [ + "header" + ], + "1": [ + "header2" + ], + "2": [ + "param" + ] + } + }, + { + "method": { + "name": "param", + "parameterTypes": [ + "java.lang.String" + ], + "returnType": "java.lang.String", + "parameters": [ + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + } + ] + }, + "request": { + "method": "GET", + "path": "/param", + "params": { + "param": [ + "value-param" + ] + }, + "headers": {}, + "consumes": [], + "produces": [] + }, + "indexToName": { + "0": [ + "param" + ] + } + }, + { + "method": { + "name": "params", + "parameterTypes": [ + "int", + "java.lang.String" + ], + "returnType": "java.lang.String", + "parameters": [ + { + "type": "int", + "items": [], + "enum": [], + "properties": {} + }, + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + } + ] + }, + "request": { + "method": "POST", + "path": "/params", + "params": { + "a": [ + "value-a" + ], + "b": [ + "value-b" + ] + }, + "headers": {}, + "consumes": [], + "produces": [] + }, + "indexToName": { + "0": [ + "a" + ], + "1": [ + "b" + ] + } + }, + { + "method": { + "name": "pathVariables", + "parameterTypes": [ + "java.lang.String", + "java.lang.String", + "java.lang.String" + ], + "returnType": "java.lang.String", + "parameters": [ + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + }, + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + }, + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + } + ] + }, + "request": { + "method": "GET", + "path": "/path-variables/{p1}/{p2}", + "params": { + "v": [ + "{2}" + ] + }, + "headers": {}, + "consumes": [], + "produces": [] + }, + "indexToName": { + "0": [ + "path1" + ], + "1": [ + "path2" + ], + "2": [ + "param" + ] + } + }, + { + "method": { + "name": "requestBodyMap", + "parameterTypes": [ + "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e", + "java.lang.String" + ], + "returnType": "org.apache.dubbo.metadata.rest.User", + "parameters": [ + { + "type": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e", + "items": [ + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + }, + { + "type": "java.lang.Object", + "items": [], + "enum": [], + "properties": {} + } + ], + "enum": [], + "properties": {} + }, + { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + } + ] + }, + "request": { + "method": "POST", + "path": "/request/body/map", + "params": { + "param": [ + "{1}" + ] + }, + "headers": {}, + "consumes": [], + "produces": [ + "application/json;charset\u003dUTF-8" + ] + }, + "indexToName": { + "0": [ + "data" + ], + "1": [ + "param" + ] + } + }, + { + "method": { + "name": "requestBodyUser", + "parameterTypes": [ + "org.apache.dubbo.metadata.rest.User" + ], + "returnType": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e", + "parameters": [ + { + "type": "org.apache.dubbo.metadata.rest.User", + "items": [], + "enum": [], + "properties": { + "name": { + "type": "java.lang.String", + "items": [], + "enum": [], + "properties": {} + }, + "id": { + "type": "java.lang.Long", + "items": [], + "enum": [], + "properties": {} + }, + "age": { + "type": "java.lang.Integer", + "items": [], + "enum": [], + "properties": {} + } + } + } + ] + }, + "request": { + "method": "POST", + "path": "/request/body/user", + "params": {}, + "headers": {}, + "consumes": [ + "application/json;charset\u003dUTF-8" + ], + "produces": [] + }, + "indexToName": { + "0": [ + "user" + ] + } + } + ] + } +] \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-definition-protobuf/src/main/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilder.java b/dubbo-metadata/dubbo-metadata-definition-protobuf/src/main/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilder.java index a74d7d7828f..77fec3790bd 100644 --- a/dubbo-metadata/dubbo-metadata-definition-protobuf/src/main/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilder.java +++ b/dubbo-metadata/dubbo-metadata-definition-protobuf/src/main/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilder.java @@ -138,7 +138,7 @@ private void validateMapType(String fieldName, String typeName) { Matcher matcher = MAP_PATTERN.matcher(typeName); if (!matcher.matches()) { throw new IllegalArgumentException("Map protobuf property " + fieldName + "of Type " + - typeName + " can't be parsed.The type name should mathch[" + MAP_PATTERN.toString() + "]."); + typeName + " can't be parsed.The type name should match[" + MAP_PATTERN.toString() + "]."); } } diff --git a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java index b4aaffdd71b..6f0010b5500 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/main/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtils.java @@ -18,7 +18,8 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeElement; -import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Objects; import java.util.Set; import static java.lang.String.valueOf; @@ -36,15 +37,27 @@ public interface ServiceAnnotationUtils { /** * The class name of @Service + * + * @deprecated Recommend {@link #DUBBO_SERVICE_ANNOTATION_TYPE} */ + @Deprecated String SERVICE_ANNOTATION_TYPE = "org.apache.dubbo.config.annotation.Service"; /** * The class name of the legacy @Service + * + * @deprecated Recommend {@link #DUBBO_SERVICE_ANNOTATION_TYPE} */ @Deprecated String LEGACY_SERVICE_ANNOTATION_TYPE = "com.alibaba.dubbo.config.annotation.Service"; + /** + * The class name of @DubboService + * + * @since 2.7.9 + */ + String DUBBO_SERVICE_ANNOTATION_TYPE = "org.apache.dubbo.config.annotation.DubboService"; + /** * the attribute name of @Service.interfaceClass() */ @@ -65,11 +78,13 @@ public interface ServiceAnnotationUtils { */ String VERSION_ATTRIBUTE_NAME = "version"; - Set SUPPORTED_ANNOTATION_TYPES = unmodifiableSet(new HashSet(asList(SERVICE_ANNOTATION_TYPE, LEGACY_SERVICE_ANNOTATION_TYPE))); + Set SUPPORTED_ANNOTATION_TYPES = unmodifiableSet(new LinkedHashSet<>(asList(DUBBO_SERVICE_ANNOTATION_TYPE, SERVICE_ANNOTATION_TYPE, LEGACY_SERVICE_ANNOTATION_TYPE))); static boolean isServiceAnnotationPresent(TypeElement annotatedType) { - return isAnnotationPresent(annotatedType, SERVICE_ANNOTATION_TYPE) || - isAnnotationPresent(annotatedType, LEGACY_SERVICE_ANNOTATION_TYPE); + return SUPPORTED_ANNOTATION_TYPES.stream() + .filter(type -> isAnnotationPresent(annotatedType, type)) + .findFirst() + .isPresent(); } static AnnotationMirror getAnnotation(TypeElement annotatedClass) { @@ -78,19 +93,20 @@ static AnnotationMirror getAnnotation(TypeElement annotatedClass) { static AnnotationMirror getAnnotation(Iterable annotationMirrors) { AnnotationMirror matchedAnnotationMirror = null; - for (AnnotationMirror annotationMirror : annotationMirrors) { - String annotationType = annotationMirror.getAnnotationType().toString(); - if (SERVICE_ANNOTATION_TYPE.equals(annotationType)) { - matchedAnnotationMirror = annotationMirror; - break; - } else if (LEGACY_SERVICE_ANNOTATION_TYPE.equals(annotationType)) { - matchedAnnotationMirror = annotationMirror; + + MAIN: + for (String supportedAnnotationType : SUPPORTED_ANNOTATION_TYPES) { // Prioritized + for (AnnotationMirror annotationMirror : annotationMirrors) { + String annotationType = annotationMirror.getAnnotationType().toString(); + if (Objects.equals(supportedAnnotationType, annotationType)) { + matchedAnnotationMirror = annotationMirror; + break MAIN; + } } } if (matchedAnnotationMirror == null) { - throw new IllegalArgumentException("The annotated element must be implemented the interface " - + SERVICE_ANNOTATION_TYPE + " or " + LEGACY_SERVICE_ANNOTATION_TYPE); + throw new IllegalArgumentException("The annotated element must be annotated any of " + SUPPORTED_ANNOTATION_TYPES); } return matchedAnnotationMirror; diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java index 9d18ddf6815..e0e95a45c9c 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/ServiceAnnotationUtilsTest.java @@ -25,10 +25,11 @@ import org.junit.jupiter.api.Test; import javax.lang.model.element.TypeElement; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import static java.util.Arrays.asList; +import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.DUBBO_SERVICE_ANNOTATION_TYPE; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.GROUP_ATTRIBUTE_NAME; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.INTERFACE_CLASS_ATTRIBUTE_NAME; import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.INTERFACE_NAME_ATTRIBUTE_NAME; @@ -65,13 +66,14 @@ protected void beforeEach() { @Test public void testConstants() { + assertEquals("org.apache.dubbo.config.annotation.DubboService", DUBBO_SERVICE_ANNOTATION_TYPE); assertEquals("org.apache.dubbo.config.annotation.Service", SERVICE_ANNOTATION_TYPE); assertEquals("com.alibaba.dubbo.config.annotation.Service", LEGACY_SERVICE_ANNOTATION_TYPE); assertEquals("interfaceClass", INTERFACE_CLASS_ATTRIBUTE_NAME); assertEquals("interfaceName", INTERFACE_NAME_ATTRIBUTE_NAME); assertEquals("group", GROUP_ATTRIBUTE_NAME); assertEquals("version", VERSION_ATTRIBUTE_NAME); - assertEquals(new HashSet(asList("org.apache.dubbo.config.annotation.Service", "com.alibaba.dubbo.config.annotation.Service")), SUPPORTED_ANNOTATION_TYPES); + assertEquals(new LinkedHashSet<>(asList("org.apache.dubbo.config.annotation.DubboService", "org.apache.dubbo.config.annotation.Service", "com.alibaba.dubbo.config.annotation.Service")), SUPPORTED_ANNOTATION_TYPES); } @Test @@ -125,7 +127,7 @@ public void testGetVersion() { @Test public void testGetGroup() { TypeElement type = getType(TestServiceImpl.class); - assertEquals("test",getGroup(getAnnotation(type))); + assertEquals("test", getGroup(getAnnotation(type))); type = getType(GenericTestService.class); assertEquals("generic", getGroup(getAnnotation(type))); diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/TypeUtilsTest.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/TypeUtilsTest.java index 55d866ebaac..091eeec1803 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/TypeUtilsTest.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/util/TypeUtilsTest.java @@ -25,6 +25,7 @@ import org.apache.dubbo.metadata.tools.GenericTestService; import org.apache.dubbo.metadata.tools.TestServiceImpl; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import javax.lang.model.element.Element; @@ -450,6 +451,7 @@ public void testListTypeElements() { } @Test + @Disabled("Failed due to github action env problem") public void testGetResource() throws URISyntaxException { URL resource = getResource(processingEnv, testType); assertNotNull(resource); diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java index 2c2245741f4..844fe840f37 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/DefaultRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import java.util.Map; @@ -25,7 +25,7 @@ * * @since 2.7.6 */ -@Service(version = "1.0.0", group = "default") +@DubboService(version = "1.0.0", group = "default") public class DefaultRestService implements RestService { @Override diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java index ed4a8a25be9..f967ad7c5f3 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/SpringRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; @@ -35,7 +35,7 @@ * * @since 2.7.6 */ -@Service(version = "2.0.0", group = "spring") +@DubboService(version = "2.0.0", group = "spring") @RestController public class SpringRestService implements RestService { diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java index f5d94b7c1e4..684d76fd3b2 100644 --- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java +++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/rest/StandardRestService.java @@ -16,7 +16,7 @@ */ package org.apache.dubbo.metadata.rest; -import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.annotation.DubboService; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; @@ -34,7 +34,7 @@ /** * JAX-RS {@link RestService} */ -@Service(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") +@DubboService(version = "3.0.0", protocol = {"dubbo", "rest"}, group = "standard") @Path("/") public class StandardRestService implements RestService { diff --git a/dubbo-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReport.java index c1e0a309bef..07cec353fe4 100644 --- a/dubbo-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReport.java @@ -18,8 +18,6 @@ package org.apache.dubbo.metadata.store.consul; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; @@ -38,12 +36,13 @@ import java.util.Collections; import java.util.List; +import static org.apache.dubbo.common.constants.ConsulConstants.DEFAULT_PORT; +import static org.apache.dubbo.common.constants.ConsulConstants.INVALID_PORT; + /** * metadata report impl for consul */ public class ConsulMetadataReport extends AbstractMetadataReport { - private static final Logger logger = LoggerFactory.getLogger(ConsulMetadataReport.class); - private static final int DEFAULT_PORT = 8500; private ConsulClient client; @@ -51,7 +50,7 @@ public ConsulMetadataReport(URL url) { super(url); String host = url.getHost(); - int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT; + int port = INVALID_PORT != url.getPort() ? url.getPort() : DEFAULT_PORT; client = new ConsulClient(host, port); } diff --git a/dubbo-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReport.java index a80c6e83ddb..7d3eab9b055 100644 --- a/dubbo-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReport.java @@ -34,8 +34,6 @@ package org.apache.dubbo.metadata.store.etcd; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; @@ -58,8 +56,6 @@ */ public class EtcdMetadataReport extends AbstractMetadataReport { - private final static Logger logger = LoggerFactory.getLogger(EtcdMetadataReport.class); - private final String root; /** diff --git a/dubbo-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java index ceaa54aa817..119522cf5a4 100644 --- a/dubbo-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java +++ b/dubbo-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.net.URI; @@ -51,6 +52,7 @@ /** * Unit test for etcd metadata report */ +@Disabled public class EtcdMetadataReportTest { private static final String TEST_SERVICE = "org.apache.dubbo.metadata.store.etcd.EtcdMetadata4TstService"; diff --git a/dubbo-metadata/dubbo-metadata-report-failover/pom.xml b/dubbo-metadata/dubbo-metadata-report-failover/pom.xml new file mode 100644 index 00000000000..18ed396f629 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/pom.xml @@ -0,0 +1,38 @@ + + + + dubbo-metadata + org.apache.dubbo + ${revision} + ../pom.xml + + 4.0.0 + + dubbo-metadata-report-failover + + + + org.apache.dubbo + dubbo-metadata-api + ${project.parent.version} + + + + \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverCondition.java b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverCondition.java new file mode 100644 index 00000000000..512c34ef7ba --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverCondition.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.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.SPI; + +@SPI("failover") +public interface FailoverCondition { + + /** + * Whether metadata should be reported. + * + * @param url registry url, eg: zookeeper://127.0.0.1:2181 + * @return true store metadata to the specified URL. + */ + boolean shouldRegister(URL url); + + /** + * Whether metadata should be read from specified url. + * + * @param url registry url, eg: zookeeper://127.0.0.1:2181 + * @return true read metadata from specified URL. + */ + boolean shouldQuery(URL url); + + /** + * Judge whether it is a local region or a local datacenter. + *

    + * Allows the local region or datacenter to be read first. + * + * @param url + * @return + */ + boolean isLocalDataCenter(URL url); + +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReport.java new file mode 100644 index 00000000000..42e4c3b0c27 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReport.java @@ -0,0 +1,581 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.RemotingConstants; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.metadata.MappingListener; +import org.apache.dubbo.metadata.MetadataInfo; +import org.apache.dubbo.metadata.definition.model.ServiceDefinition; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.MetadataReportFactory; +import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; +import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN; + +/** + * @author yiji@apache.org + */ +public class FailoverMetadataReport extends StrategyMetadataReport { + + private static final Logger logger = LoggerFactory.getLogger(FailoverMetadataReport.class); + + // proxy metadata report protocol, eg: zookeeper + private static final String PROTOCOL_KEY = "protocol"; + + private static final String CLUSTER_KEY = "clusters"; + + // A cluster may have multiple instances + private static final String HOST_KEY = "hosts"; + + private static final Pattern HOST_SPLIT_PATTERN = Pattern.compile("\\s*[|:]+\\s*"); + + // The metadata address of the agent. + private List failoverUrls; + + // The metadata report instance. + private List proxyReports; + + // Local priority metadata center + private MetadataReportHolder localDataCenterReportHolder; + + public FailoverMetadataReport(URL url) { + super(url); + this.failoverUrls = fetchBackupUrls(); + this.proxyReports = buildProxyReports(); + } + + protected List fetchBackupUrls() { + String protocol = url.getParameter(PROTOCOL_KEY); + if (protocol == null || !ExtensionLoader.getExtensionLoader(MetadataReportFactory.class).hasExtension(protocol)) { + throw new IllegalArgumentException( + "No '" + protocol + + "' metadata report extension found, please check if metadata report module dependencies are included."); + } + + List urls = new ArrayList<>(); + + String clusters = this.url.getParameter(CLUSTER_KEY); + String backupHost = this.url.getParameter(HOST_KEY); + URL url = this.url.removeParameters(CLUSTER_KEY, HOST_KEY, PROTOCOL_KEY).setProtocol(protocol); + + URL metadataURL = url; + if (backupHost != null && backupHost.length() > 0) { + metadataURL = metadataURL.addParameter(RemotingConstants.BACKUP_KEY, backupHost); + } + urls.add(metadataURL); + + if (clusters != null && (clusters = clusters.trim()).length() > 0) { + String[] addresses = REGISTRY_SPLIT_PATTERN.split(clusters); + for (String address : addresses) { + /** + * find multiple cluster hosts, supports multiple + * metadata report center read and write operations. + */ + String[] hosts = COMMA_SPLIT_PATTERN.split(address); + if (hosts.length > 0) { + String node = hosts[0]; + // contains user name and password with address ? + String username = null, password = null; + int index = node.indexOf("@"); + if (index > 0) { + String[] authority = HOST_SPLIT_PATTERN.split(node.substring(0, index)); + username = authority[0]; + password = authority[1]; + node = node.substring(index + 1); + } + + String[] hostInfo = HOST_SPLIT_PATTERN.split(node); + String host = hostInfo[0]; + int port = Integer.parseInt(hostInfo[1]); + URL clusterURL = new URL(protocol, username, password, host, port, url.getPath(), url.getParameters()); + /** + * append backup address if required, + * the same cluster may have more than one node. + */ + if (hosts.length > 1) { + StringBuilder buffer = new StringBuilder(); + for (int i = 1; i < hosts.length; i++) { + if (i > 1) { + buffer.append(","); + } + buffer.append(hosts[i]); + } + clusterURL = clusterURL.addParameters(RemotingConstants.BACKUP_KEY, buffer.toString()); + } + urls.add(clusterURL); + } + } + } + return urls; + } + + protected List buildProxyReports() { + List reports = new ArrayList<>(); + if (this.failoverUrls != null && !this.failoverUrls.isEmpty()) { + ExtensionLoader factoryLoader = ExtensionLoader.getExtensionLoader(MetadataReportFactory.class); + for (URL url : this.failoverUrls) { + try { + MetadataReportHolder holder = new MetadataReportHolder(url, + factoryLoader.getExtension(url.getProtocol()).getMetadataReport(url)); + reports.add(holder); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw new RuntimeException("Failed to create + '" + url.getProtocol() + "' metadata report extension instance", e); + } + if (logger.isWarnEnabled()) { + logger.warn("Failed to create + '" + url.getProtocol() + + "' metadata report extension instance, check=false found."); + } + } + } + } + + Collections.shuffle(reports); + + /** + * Select the local priority metadata cluster. + * In order to prevent clients from all connecting + * to the same cluster, random sorting has been done. + */ + reports.forEach(holder -> { + if (isLocalDataCenter(holder.url)) { + this.localDataCenterReportHolder = holder; + } + }); + + return reports; + } + + @Override + public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { + this.proxyReports.forEach((holder -> { + if (shouldRegister(holder.url)) { + try { + holder.report.storeProviderMetadata(providerMetadataIdentifier, serviceDefinition); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw e; + } + } + } else { + if (logger.isInfoEnabled()) { + logger.info("Cancel to store provider metadata, register is false. url " + holder.url); + } + } + })); + } + + @Override + public void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) { + this.proxyReports.forEach(holder -> { + if (shouldRegister(holder.url)) { + try { + holder.report.storeConsumerMetadata(consumerMetadataIdentifier, serviceParameterMap); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw e; + } + } + } else { + if (logger.isInfoEnabled()) { + logger.info("Cancel to store consumer metadata, register is false. url " + holder.url); + } + } + }); + } + + @Override + public void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { + this.proxyReports.forEach(holder -> { + if (shouldRegister(holder.url)) { + try { + holder.report.publishAppMetadata(identifier, metadataInfo); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw e; + } + } + } else { + if (logger.isInfoEnabled()) { + logger.info("Cancel to publish app metadata, register is false. url " + holder.url); + } + } + }); + } + + @Override + public String getServiceDefinition(MetadataIdentifier metadataIdentifier) { + /** + * Support local region or datacenter to read first, + * If current region or datacenter failed, it will be demoted to another region or datacenter. + */ + MetadataReportHolder localReportHolder = this.localDataCenterReportHolder; + if (localReportHolder != null && shouldQuery(localReportHolder.url)) { + try { + String definition = localReportHolder.report.getServiceDefinition(metadataIdentifier); + if (definition != null && definition.length() > 0) { + return definition; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get service definition from local metadata report center, url " + localReportHolder.url); + } + } + } + + for (MetadataReportHolder holder : proxyReports) { + /** + * Skip the local region or datacenter read, + * which was queried already. + */ + if (localReportHolder != null + && holder.url == localReportHolder.url) { + continue; + } + + if (shouldQuery(holder.url)) { + try { + String definition = holder.report.getServiceDefinition(metadataIdentifier); + if (definition != null && definition.length() > 0) { + return definition; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get service definition from metadata report center, url " + holder.url); + } + } + } + + // should never happened. + if (logger.isInfoEnabled()) { + logger.info("Cancel to get service definition, should query is false. url " + holder.url); + } + } + + return null; + } + + @Override + public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { + /** + * Support local region or datacenter to read first, + * If current region or datacenter failed, it will be demoted to another region or datacenter. + */ + MetadataReportHolder localReportHolder = this.localDataCenterReportHolder; + if (localReportHolder != null && shouldQuery(localReportHolder.url)) { + try { + MetadataInfo metadataInfo = localReportHolder.report.getAppMetadata(identifier, instanceMetadata); + if (metadataInfo != null) { + return metadataInfo; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get app metadata from local metadata report center, url " + localReportHolder.url); + } + } + } + + for (MetadataReportHolder holder : proxyReports) { + /** + * Skip the local region or datacenter read, + * which was queried already. + */ + if (localReportHolder != null + && holder.url == localReportHolder.url) { + continue; + } + + if (shouldQuery(holder.url)) { + try { + MetadataInfo metadataInfo = holder.report.getAppMetadata(identifier, instanceMetadata); + if (metadataInfo != null) { + return metadataInfo; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get app metadata from metadata report center, url " + holder.url); + } + } + } + + // should never happened. + if (logger.isInfoEnabled()) { + logger.info("Cancel to get app metadata, should query is false. url " + holder.url); + } + } + + return null; + } + + @Override + public Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { + /** + * Support local region or datacenter to read first, + * If current region or datacenter failed, it will be demoted to another region or datacenter. + */ + MetadataReportHolder localReportHolder = this.localDataCenterReportHolder; + if (localReportHolder != null && shouldQuery(localReportHolder.url)) { + try { + Set appMapping = localReportHolder.report.getServiceAppMapping(serviceKey, listener, url); + if (appMapping != null && !appMapping.isEmpty()) { + return appMapping; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get service mapping from local metadata report center, url " + localReportHolder.url); + } + } + } + + for (MetadataReportHolder holder : proxyReports) { + /** + * Skip the local region or datacenter read, + * which was queried already. + */ + if (localReportHolder != null + && holder.url == localReportHolder.url) { + continue; + } + + if (shouldQuery(holder.url)) { + try { + Set appMapping = holder.report.getServiceAppMapping(serviceKey, listener, url); + if (appMapping != null && !appMapping.isEmpty()) { + return appMapping; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get service mapping from metadata report center, url " + holder.url); + } + } + } + + // should never happened. + if (logger.isInfoEnabled()) { + logger.info("Cancel to get service mapping, should query is false. url " + holder.url); + } + } + + return Collections.EMPTY_SET; + } + + @Override + public void registerServiceAppMapping(String serviceKey, String application, URL url) { + this.proxyReports.forEach(holder -> { + if (shouldRegister(holder.url)) { + try { + holder.report.registerServiceAppMapping(serviceKey, application, url); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw e; + } + } + } else { + if (logger.isInfoEnabled()) { + logger.info("Cancel to register service app mapping, register is false. url " + holder.url); + } + } + }); + } + + @Override + public void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) { + this.proxyReports.forEach(holder -> { + if (shouldRegister(holder.url)) { + try { + holder.report.saveServiceMetadata(metadataIdentifier, url); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw e; + } + } + } else { + if (logger.isInfoEnabled()) { + logger.info("Cancel to register service app mapping, register is false. url " + holder.url); + } + } + }); + } + + @Override + public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set urls) { + this.proxyReports.forEach(holder -> { + if (shouldRegister(holder.url)) { + try { + holder.report.saveSubscribedData(subscriberMetadataIdentifier, urls); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw e; + } + } + } else { + if (logger.isInfoEnabled()) { + logger.info("Cancel to register service app mapping, register is false. url " + holder.url); + } + } + }); + } + + @Override + public void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) { + this.proxyReports.forEach(holder -> { + if (shouldRegister(holder.url)) { + try { + holder.report.removeServiceMetadata(metadataIdentifier); + } catch (Exception e) { + if (url.getParameter(CHECK_KEY, true)) { + throw e; + } + } + } + }); + } + + @Override + public List getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { + /** + * Support local region or datacenter to read first, + * If current region or datacenter failed, it will be demoted to another region or datacenter. + */ + MetadataReportHolder localReportHolder = this.localDataCenterReportHolder; + if (localReportHolder != null && shouldQuery(localReportHolder.url)) { + try { + List exportedURLs = localReportHolder.report.getExportedURLs(metadataIdentifier); + if (exportedURLs != null && !exportedURLs.isEmpty()) { + return exportedURLs; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get exported urls from local metadata report center, url " + localReportHolder.url); + } + } + } + + for (MetadataReportHolder holder : proxyReports) { + /** + * Skip the local region or datacenter read, + * which was queried already. + */ + if (localReportHolder != null + && holder.url == localReportHolder.url) { + continue; + } + + if (shouldQuery(holder.url)) { + try { + List exportedURLs = holder.report.getExportedURLs(metadataIdentifier); + if (exportedURLs != null && !exportedURLs.isEmpty()) { + return exportedURLs; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get exported urls from metadata report center, url " + holder.url); + } + } + } + + // should never happened. + if (logger.isInfoEnabled()) { + logger.info("Cancel to get exported urls, should query is false. url " + holder.url); + } + } + + return Collections.EMPTY_LIST; + } + + @Override + public List getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { + /** + * Support local region or datacenter to read first, + * If current region or datacenter failed, it will be demoted to another region or datacenter. + */ + MetadataReportHolder localReportHolder = this.localDataCenterReportHolder; + if (localReportHolder != null && shouldQuery(localReportHolder.url)) { + try { + List subscribedURLs = localReportHolder.report.getSubscribedURLs(subscriberMetadataIdentifier); + if (subscribedURLs != null && !subscribedURLs.isEmpty()) { + return subscribedURLs; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get subscribed urls from local metadata report center, url " + localReportHolder.url); + } + } + } + + for (MetadataReportHolder holder : proxyReports) { + /** + * Skip the local region or datacenter read, + * which was queried already. + */ + if (localReportHolder != null + && holder.url == localReportHolder.url) { + continue; + } + + if (shouldQuery(holder.url)) { + try { + List subscribedURLs = holder.report.getSubscribedURLs(subscriberMetadataIdentifier); + if (subscribedURLs != null && !subscribedURLs.isEmpty()) { + return subscribedURLs; + } + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to get subscribed urls from metadata report center, url " + holder.url); + } + } + } + + // should never happened. + if (logger.isInfoEnabled()) { + logger.info("Cancel to get subscribed urls, should query is false. url " + holder.url); + } + } + + return Collections.EMPTY_LIST; + } + + public List getProxyReports() { + return proxyReports; + } + + class MetadataReportHolder { + + final URL url; + final MetadataReport report; + + public MetadataReportHolder(URL url, MetadataReport report) { + this.url = url; + this.report = report; + } + } +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReportFactory.java b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReportFactory.java new file mode 100644 index 00000000000..b47c14c983c --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReportFactory.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory; + +public class FailoverMetadataReportFactory extends AbstractMetadataReportFactory { + + @Override + protected MetadataReport createMetadataReport(URL url) { + return new FailoverMetadataReport(url); + } +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/StrategyMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/StrategyMetadataReport.java new file mode 100644 index 00000000000..ef7636c9160 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/main/java/org/apache/dubbo/metadata/store/failover/StrategyMetadataReport.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.metadata.report.MetadataReport; + +/** + * @author yiji@apache.org + */ +public abstract class StrategyMetadataReport implements MetadataReport { + + // failover configured url, eg: failover://127.0.1:2181?backup=localhost:2181|localhost:2181 + protected URL url; + + protected static final String STRATEGY_KEY = "strategy"; + + // proxy metadata report strategy, used to decide whether to write or read metadata + protected FailoverCondition strategy; + + protected ExtensionLoader failoverLoader = ExtensionLoader.getExtensionLoader(FailoverCondition.class); + + public StrategyMetadataReport(URL url) { + if (url == null) { + throw new IllegalArgumentException("url is required."); + } + this.url = url; + createFailoverStrategy(url); + } + + protected void createFailoverStrategy(URL url) { + String strategy = url.getParameter(STRATEGY_KEY); + if (strategy != null) { + if (!failoverLoader.hasExtension(strategy)) { + throw new IllegalArgumentException("No '" + strategy + "' failover condition extension found."); + } + this.strategy = failoverLoader.getExtension(strategy); + } + } + + /** + * Whether metadata should be reported. + * + * @param url registry url, eg: zookeeper://127.0.0.1:2181 + * @return true store metadata to the specified URL. + */ + protected boolean shouldRegister(URL url) { + return this.strategy == null || this.strategy.shouldRegister(url); + } + + /** + * Whether metadata should be read from specified url. + * + * @param url registry url, eg: zookeeper://127.0.0.1:2181 + * @return true read metadata from specified URL. + */ + protected boolean shouldQuery(URL url) { + return this.strategy == null || this.strategy.shouldQuery(url); + } + + /** + * Judge whether it is a local region or a local datacenter. + *

    + * Allows the local region or datacenter to be read first. + * + * @param url + * @return + */ + protected boolean isLocalDataCenter(URL url) { + return this.strategy == null || this.strategy.isLocalDataCenter(url); + } + +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/dubbo-metadata/dubbo-metadata-report-failover/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory new file mode 100644 index 00000000000..e530c5e4fd2 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory @@ -0,0 +1 @@ +failover=org.apache.dubbo.metadata.store.failover.FailoverMetadataReportFactory diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReportTest.java new file mode 100644 index 00000000000..b57bd23b58a --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/FailoverMetadataReportTest.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.metadata.MetadataInfo; +import org.apache.dubbo.metadata.definition.model.ServiceDefinition; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.MetadataReportFactory; +import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; +import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class FailoverMetadataReportTest { + + private ExtensionLoader reportLoader = ExtensionLoader.getExtensionLoader(MetadataReportFactory.class); + + private URL mockURL = URL.valueOf("failover://127.0.0.1:2181?clusters=localhost:3181&protocol=mock"); + + @AfterEach + void tearDown() { + clearFailoverReport(); + clearFailoverFactory(); + } + + @Test + public void testReadWriteAllMetadataReport() { + URL url = mockURL.addParameter("strategy", "all"); + FailoverMetadataReport report = getFailoverReport(url); + Assertions.assertNotNull(report.getProxyReports(), "metadata reports should not be null."); + Assertions.assertEquals(2, report.getProxyReports().size(), + "expect 2 metadata report, actual " + report.getProxyReports().size()); + + MetadataIdentifier identifier = new MetadataIdentifier("helloService", null, null, null, "test"); + ServiceDefinition definition = new ServiceDefinition(); + definition.setCanonicalName("helloService"); + report.storeProviderMetadata(identifier, definition); + Assertions.assertNotNull(report.getServiceDefinition(identifier)); + // assert all metadata report write already. + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + Assertions.assertNotNull(holder.report.getServiceDefinition(identifier)); + } + + HashMap parameterMap = new HashMap(); + report.storeConsumerMetadata(identifier, parameterMap); + // assert all metadata report write already. + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + Assertions.assertEquals(parameterMap, ((MockMetadataReport) holder.report).consumerMetadata.get(identifier)); + } + + SubscriberMetadataIdentifier subscribeIdentifier = new SubscriberMetadataIdentifier("test", "1.0"); + MetadataInfo metadataInfo = new MetadataInfo(subscribeIdentifier.getApplication(), subscribeIdentifier.getRevision(), null); + report.publishAppMetadata(subscribeIdentifier, metadataInfo); + Assertions.assertEquals(metadataInfo, report.getAppMetadata(subscribeIdentifier, null)); + // assert all metadata report write already. + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + Assertions.assertEquals(metadataInfo, holder.report.getAppMetadata(subscribeIdentifier, null)); + } + + report.registerServiceAppMapping("helloService", "test", null); + Set appNames = report.getServiceAppMapping("helloService", null, null); + Assertions.assertEquals(appNames, report.getServiceAppMapping("helloService", null, null)); + // assert all metadata report write already. + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + Assertions.assertEquals(appNames, holder.report.getServiceAppMapping("helloService", null, null)); + } + + ServiceMetadataIdentifier serviceIdentifier = new ServiceMetadataIdentifier("helloService", null, null, null, "1.0", "dubbo"); + report.saveServiceMetadata(serviceIdentifier, url); + Assertions.assertNotNull(report.getExportedURLs(serviceIdentifier)); + // assert all metadata report write already. + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + Assertions.assertNotNull(holder.report.getExportedURLs(serviceIdentifier)); + } + + report.saveSubscribedData(subscribeIdentifier, new HashSet<>()); + Assertions.assertNotNull(report.getSubscribedURLs(subscribeIdentifier)); + // assert all metadata report write already. + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + Assertions.assertNotNull(holder.report.getSubscribedURLs(subscribeIdentifier)); + } + } + + @Test + public void testLocalDataCenterMetadataReport() { + URL url = mockURL.addParameter("strategy", "local"); + FailoverMetadataReport report = getFailoverReport(url); + Assertions.assertNotNull(report.getProxyReports(), "metadata reports should not be null."); + Assertions.assertEquals(2, report.getProxyReports().size(), + "expect 2 metadata report, actual " + report.getProxyReports().size()); + + MetadataReport localReport = null, failoverReport = null; + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + if (holder.url.getBackupAddress().contains(url.getAddress())) { + localReport = holder.report; + } else { + failoverReport = holder.report; + } + } + Assertions.assertNotNull(localReport); + Assertions.assertNotNull(failoverReport); + + MetadataIdentifier identifier = new MetadataIdentifier("helloService", null, null, null, "test"); + ServiceDefinition definition = new ServiceDefinition(); + definition.setCanonicalName("helloService"); + report.storeProviderMetadata(identifier, definition); + + // assert local metadata report write already. + Assertions.assertNotNull(report.getServiceDefinition(identifier)); + Assertions.assertNotNull(localReport.getServiceDefinition(identifier)); + Assertions.assertNull(failoverReport.getServiceDefinition(identifier)); + + HashMap parameterMap = new HashMap(); + report.storeConsumerMetadata(identifier, parameterMap); + // assert local metadata report write already. + Assertions.assertEquals(parameterMap, ((MockMetadataReport) localReport).consumerMetadata.get(identifier)); + Assertions.assertNotEquals(parameterMap, ((MockMetadataReport) failoverReport).consumerMetadata.get(identifier)); + + SubscriberMetadataIdentifier subscribeIdentifier = new SubscriberMetadataIdentifier("test", "1.0"); + MetadataInfo metadataInfo = new MetadataInfo(subscribeIdentifier.getApplication(), subscribeIdentifier.getRevision(), null); + report.publishAppMetadata(subscribeIdentifier, metadataInfo); + // assert all metadata report write already. + Assertions.assertEquals(metadataInfo, report.getAppMetadata(subscribeIdentifier, null)); + Assertions.assertEquals(metadataInfo, localReport.getAppMetadata(subscribeIdentifier, null)); + Assertions.assertNotEquals(metadataInfo, failoverReport.getAppMetadata(subscribeIdentifier, null)); + + report.registerServiceAppMapping("helloService", "test", null); + Set appNames = report.getServiceAppMapping("helloService", null, null); + + // assert local metadata report write already. + Assertions.assertEquals(appNames, report.getServiceAppMapping("helloService", null, null)); + Assertions.assertEquals(appNames, localReport.getServiceAppMapping("helloService", null, null)); + Assertions.assertNotEquals(appNames, failoverReport.getServiceAppMapping("helloService", null, null)); + + ServiceMetadataIdentifier serviceIdentifier = new ServiceMetadataIdentifier("helloService", null, null, null, "1.0", "dubbo"); + report.saveServiceMetadata(serviceIdentifier, url); + // assert local metadata report write already. + Assertions.assertNotNull(report.getExportedURLs(serviceIdentifier)); + Assertions.assertNotNull(localReport.getExportedURLs(serviceIdentifier)); + Assertions.assertNull(failoverReport.getExportedURLs(serviceIdentifier)); + + Set urls = new HashSet<>(); + urls.add(url.toFullString()); + report.saveSubscribedData(subscribeIdentifier, urls); + // assert local metadata report write already. + Assertions.assertEquals(new ArrayList<>(urls), report.getSubscribedURLs(subscribeIdentifier)); + Assertions.assertEquals(new ArrayList<>(urls), localReport.getSubscribedURLs(subscribeIdentifier)); + Assertions.assertNotEquals(new ArrayList<>(urls), failoverReport.getSubscribedURLs(subscribeIdentifier)); + } + + protected FailoverMetadataReport getFailoverReport(URL url) { + MetadataReportFactory reportFactory = reportLoader.getExtension(url.getProtocol()); + Assertions.assertTrue(reportFactory instanceof FailoverMetadataReportFactory, + "expect " + FailoverMetadataReportFactory.class.getName() + " instance type, " + + "actual " + reportFactory.getClass().getName() + " instance type"); + + MetadataReport report = reportFactory.getMetadataReport(url); + Assertions.assertTrue(report instanceof FailoverMetadataReport, + "expect " + FailoverMetadataReport.class.getName() + " instance type, " + + "actual " + report.getClass().getName() + " instance type"); + + FailoverMetadataReport failover = (FailoverMetadataReport) report; + return failover; + } + + private void clearFailoverReport() { + FailoverMetadataReport report = getFailoverReport(mockURL); + for (FailoverMetadataReport.MetadataReportHolder holder : report.getProxyReports()) { + if (holder.report instanceof MockMetadataReport) { + ((MockMetadataReport) (holder.report)).reset(); + } + } + } + + private void clearFailoverFactory() { + MetadataReportFactory factory = reportLoader.getExtension(mockURL.getProtocol()); + try { + Field reportCache = AbstractMetadataReportFactory.class.getDeclaredField("SERVICE_STORE_MAP"); + if (!reportCache.isAccessible()) { + ReflectUtils.makeAccessible(reportCache); + } + Map serviceStore = (Map) reportCache.get(factory); + if (serviceStore != null) { + for (Iterator> iterator = serviceStore.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry entry = iterator.next(); + if (entry.getKey().startsWith(mockURL.getProtocol())) { + iterator.remove(); + } + } + } + } catch (NoSuchFieldException | IllegalAccessException ignored) { + } + } + +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockAllFailoverCondition.java similarity index 62% rename from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java rename to dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockAllFailoverCondition.java index bc9748c7c77..1aa0e7c80f8 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockAllFailoverCondition.java @@ -14,25 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.registry.client; +package org.apache.dubbo.metadata.store.failover; -import org.apache.dubbo.registry.integration.RegistryProtocolListener; -import org.apache.dubbo.rpc.Exporter; -import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.common.URL; -public class ServiceDiscoveryRegistryProtocolListener implements RegistryProtocolListener { - @Override - public void onExport(RegistryProtocol registryProtocol, Exporter exporter) { - - } +public class MockAllFailoverCondition extends MockLocalFailoverCondition { @Override - public void onRefer(RegistryProtocol registryProtocol, Invoker invoker) { - + public boolean shouldRegister(URL url) { + return true; } @Override - public void onDestroy() { - + public boolean isLocalDataCenter(URL url) { + // we don't care about local datacenter first. + return false; } -} +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockLocalFailoverCondition.java b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockLocalFailoverCondition.java new file mode 100644 index 00000000000..1efa1acf92d --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockLocalFailoverCondition.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; + +/** + * @author yiji@apache.org + */ +public class MockLocalFailoverCondition implements FailoverCondition { + + @Override + public boolean shouldRegister(URL url) { + // we just register same datacenter. + return isLocalDataCenter(url); + } + + @Override + public boolean shouldQuery(URL url) { + // we want read any metadata report server. + return true; + } + + @Override + public boolean isLocalDataCenter(URL url) { + // we mock current datacenter is `127.0.0.1:2181` + String current = "127.0.0.1:2181"; + return url.getBackupAddress().contains(current); + } + +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockMetadataReport.java new file mode 100644 index 00000000000..a2648124b75 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockMetadataReport.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.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.metadata.MappingListener; +import org.apache.dubbo.metadata.MetadataInfo; +import org.apache.dubbo.metadata.definition.model.ServiceDefinition; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier; +import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; + +public class MockMetadataReport implements MetadataReport { + + public URL url; + + public ConcurrentMap providerMetadata = new ConcurrentHashMap<>(); + public ConcurrentMap appMetadata = new ConcurrentHashMap<>(); + public ConcurrentMap> appMapping = new ConcurrentHashMap<>(); + public ConcurrentMap> consumerMetadata = new ConcurrentHashMap<>(); + public ConcurrentMap> serviceMetadata = new ConcurrentHashMap<>(); + public ConcurrentMap> subscribeMetadata = new ConcurrentHashMap<>(); + + public MockMetadataReport(URL url) { + this.url = url; + } + + @Override + public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { + providerMetadata.put(providerMetadataIdentifier, serviceDefinition); + } + + @Override + public String getServiceDefinition(MetadataIdentifier metadataIdentifier) { + ServiceDefinition definition = providerMetadata.get(metadataIdentifier); + return definition == null ? null : definition.toString(); + } + + @Override + public void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) { + appMetadata.put(identifier, metadataInfo); + } + + @Override + public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map instanceMetadata) { + return appMetadata.get(identifier); + } + + @Override + public Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { + return appMapping.get(serviceKey); + } + + @Override + public void registerServiceAppMapping(String serviceKey, String application, URL url) { + appMapping.putIfAbsent(serviceKey, new ConcurrentHashSet<>()); + Set appNames = appMapping.get(serviceKey); + appNames.add(application); + } + + @Override + public void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) { + consumerMetadata.put(consumerMetadataIdentifier, serviceParameterMap); + } + + @Override + public List getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) { + return serviceMetadata.get(metadataIdentifier); + } + + @Override + public void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) { + serviceMetadata.putIfAbsent(metadataIdentifier, new CopyOnWriteArrayList<>()); + List urls = serviceMetadata.get(metadataIdentifier); + urls.add(url.toFullString()); + } + + @Override + public void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) { + serviceMetadata.remove(metadataIdentifier); + } + + @Override + public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set urls) { + subscribeMetadata.putIfAbsent(subscriberMetadataIdentifier, new CopyOnWriteArraySet()); + Set metadataUrls = subscribeMetadata.get(subscriberMetadataIdentifier); + metadataUrls.addAll(urls); + } + + @Override + public List getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) { + Set urls = subscribeMetadata.get(subscriberMetadataIdentifier); + if (urls == null) { return Collections.EMPTY_LIST; } + return new ArrayList<>(urls); + } + + public void reset() { + providerMetadata.clear(); + appMetadata.clear(); + appMapping.clear(); + consumerMetadata.clear(); + serviceMetadata.clear(); + subscribeMetadata.clear(); + } +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockMetadataReportFactory.java b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockMetadataReportFactory.java new file mode 100644 index 00000000000..0c1c73eed13 --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/test/java/org/apache/dubbo/metadata/store/failover/MockMetadataReportFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.metadata.store.failover; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory; + +public class MockMetadataReportFactory extends AbstractMetadataReportFactory { + + @Override + protected MetadataReport createMetadataReport(URL url) { + return new MockMetadataReport(url); + } + +} \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/dubbo-metadata/dubbo-metadata-report-failover/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory new file mode 100644 index 00000000000..3336979a19e --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory @@ -0,0 +1 @@ +mock=org.apache.dubbo.metadata.store.failover.MockMetadataReportFactory \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-failover/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.store.failover.FailoverCondition b/dubbo-metadata/dubbo-metadata-report-failover/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.store.failover.FailoverCondition new file mode 100644 index 00000000000..ee09f7ce59e --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-failover/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.store.failover.FailoverCondition @@ -0,0 +1,2 @@ +local=org.apache.dubbo.metadata.store.failover.MockLocalFailoverCondition +all=org.apache.dubbo.metadata.store.failover.MockAllFailoverCondition \ No newline at end of file diff --git a/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosConfigServiceWrapper.java b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosConfigServiceWrapper.java new file mode 100644 index 00000000000..e5f40743c5b --- /dev/null +++ b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosConfigServiceWrapper.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.dubbo.metadata.store.nacos; + +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.exception.NacosException; + +public class NacosConfigServiceWrapper { + + private static final String INNERCLASS_SYMBOL = "$"; + + private static final String INNERCLASS_COMPATIBLE_SYMBOL = "___"; + + private ConfigService configService; + + public NacosConfigServiceWrapper(ConfigService configService) { + this.configService = configService; + } + + public boolean publishConfig(String dataId, String group, String content) throws NacosException { + return configService.publishConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), content); + } + + public boolean removeConfig(String dataId, String group) throws NacosException { + return configService.removeConfig(handleInnerSymbol(dataId), handleInnerSymbol(group)); + } + + public String getConfig(String dataId, String group, long timeout) throws NacosException { + return configService.getConfig(handleInnerSymbol(dataId), handleInnerSymbol(group), timeout); + } + + /** + * see {@link com.alibaba.nacos.client.config.utils.ParamUtils#isValid(java.lang.String)} + */ + private String handleInnerSymbol(String dataId) { + if (dataId == null) { + return null; + } + return dataId.replace(INNERCLASS_SYMBOL, INNERCLASS_COMPATIBLE_SYMBOL); + } +} diff --git a/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java index 707d2b8261c..c59f3ba8231 100644 --- a/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java @@ -18,8 +18,6 @@ package org.apache.dubbo.metadata.store.nacos; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; @@ -30,7 +28,6 @@ import org.apache.dubbo.rpc.RpcException; import com.alibaba.nacos.api.NacosFactory; -import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; import java.util.ArrayList; @@ -67,9 +64,7 @@ */ public class NacosMetadataReport extends AbstractMetadataReport { - private static final Logger logger = LoggerFactory.getLogger(NacosMetadataReport.class); - - private ConfigService configService; + private NacosConfigServiceWrapper configService; /** * The group used to store metadata in Nacos @@ -83,10 +78,10 @@ public NacosMetadataReport(URL url) { group = url.getParameter(GROUP_KEY, DEFAULT_ROOT); } - public ConfigService buildConfigService(URL url) { + public NacosConfigServiceWrapper buildConfigService(URL url) { Properties nacosProperties = buildNacosProperties(url); try { - configService = NacosFactory.createConfigService(nacosProperties); + configService = new NacosConfigServiceWrapper(NacosFactory.createConfigService(nacosProperties)); } catch (NacosException e) { if (logger.isErrorEnabled()) { logger.error(e.getErrMsg(), e); @@ -225,7 +220,7 @@ private void deleteMetadata(BaseMetadataIdentifier identifier) { private String getConfig(BaseMetadataIdentifier identifier) { try { - return configService.getConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, 300); + return configService.getConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, 3000L); } catch (Throwable t) { logger.error("Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t); throw new RpcException("Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t); diff --git a/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java index c00be049b72..93e9eaf0365 100644 --- a/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java @@ -17,8 +17,6 @@ package org.apache.dubbo.metadata.store.redis; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier; import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum; @@ -53,7 +51,14 @@ public class RedisMetadataReport extends AbstractMetadataReport { private final static String REDIS_DATABASE_KEY = "database"; - private final static Logger logger = LoggerFactory.getLogger(RedisMetadataReport.class); + /** + * maximum number of retries + */ + private final static int MAX_ATTEMPTS = 2; + /** + * the default slot of the redis database + */ + private final static int DEFAULT_REDIS_DATABASE_SLOT = 0; JedisPool pool; Set jedisClusterNodes; @@ -64,6 +69,7 @@ public class RedisMetadataReport extends AbstractMetadataReport { public RedisMetadataReport(URL url) { super(url); timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT); + password = url.getPassword(); if (url.getParameter(CLUSTER_KEY, false)) { jedisClusterNodes = new HashSet(); List urls = url.getBackupUrls(); @@ -71,8 +77,8 @@ public RedisMetadataReport(URL url) { jedisClusterNodes.add(new HostAndPort(tmpUrl.getHost(), tmpUrl.getPort())); } } else { - int database = url.getParameter(REDIS_DATABASE_KEY, 0); - pool = new JedisPool(new JedisPoolConfig(), url.getHost(), url.getPort(), timeout, url.getPassword(), database); + int database = url.getParameter(REDIS_DATABASE_KEY, DEFAULT_REDIS_DATABASE_SLOT); + pool = new JedisPool(new JedisPoolConfig(), url.getHost(), url.getPort(), timeout, password, database); } } @@ -129,7 +135,8 @@ private void storeMetadata(BaseMetadataIdentifier metadataIdentifier, String v) } private void storeMetadataInCluster(BaseMetadataIdentifier metadataIdentifier, String v) { - try (JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig())) { + try (JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, timeout, timeout, MAX_ATTEMPTS, password, + new GenericObjectPoolConfig())) { jedisCluster.set(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG, v); } catch (Throwable e) { logger.error("Failed to put " + metadataIdentifier + " to redis cluster " + v + ", cause: " + e.getMessage(), e); @@ -155,7 +162,8 @@ private void deleteMetadata(BaseMetadataIdentifier metadataIdentifier) { } private void deleteMetadataInCluster(BaseMetadataIdentifier metadataIdentifier) { - try (JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig())) { + try (JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, timeout, timeout, MAX_ATTEMPTS, password, + new GenericObjectPoolConfig())) { jedisCluster.del(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG); } catch (Throwable e) { logger.error("Failed to delete " + metadataIdentifier + " from redis cluster , cause: " + e.getMessage(), e); @@ -181,7 +189,8 @@ private String getMetadata(BaseMetadataIdentifier metadataIdentifier) { } private String getMetadataInCluster(BaseMetadataIdentifier metadataIdentifier) { - try (JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig())) { + try (JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, timeout, timeout, MAX_ATTEMPTS, password, + new GenericObjectPoolConfig())) { return jedisCluster.get(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG); } catch (Throwable e) { logger.error("Failed to get " + metadataIdentifier + " from redis cluster , cause: " + e.getMessage(), e); diff --git a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java index b221a5b6071..9793eb97780 100644 --- a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java +++ b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java @@ -18,8 +18,7 @@ import com.google.gson.Gson; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; @@ -52,8 +51,6 @@ */ public class ZookeeperMetadataReport extends AbstractMetadataReport { - private final static Logger logger = LoggerFactory.getLogger(ZookeeperMetadataReport.class); - private final String root; final ZookeeperClient zkClient; @@ -162,22 +159,35 @@ public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map< public Set getServiceAppMapping(String serviceKey, MappingListener listener, URL url) { Set appNameSet = new HashSet<>(); String path = toRootDir() + serviceKey; - appNameSet.addAll(zkClient.getChildren(path)); + + List appNameList; if (null == listenerMap.get(path)) { - ChildListener zkListener = new ChildListener() { - @Override - public void childChanged(String path, List children) { - MappingChangedEvent event = new MappingChangedEvent(); - event.setServiceKey(serviceKey); - event.setApps(null != children ? new HashSet<>(children): null); - listener.onEvent(event); - } - }; - zkClient.addChildListener(path, zkListener); - listenerMap.put(path, zkListener); + zkClient.create(path, false); + appNameList = addServiceMappingListener(path, serviceKey, listener); + } else { + appNameList = zkClient.getChildren(path); + } + + if (!CollectionUtils.isEmpty(appNameList)) { + appNameSet.addAll(appNameList); } return appNameSet; } + + private List addServiceMappingListener(String path, String serviceKey, MappingListener listener) { + ChildListener zkListener = new ChildListener() { + @Override + public void childChanged(String path, List children) { + MappingChangedEvent event = new MappingChangedEvent(); + event.setServiceKey(serviceKey); + event.setApps(null != children ? new HashSet<>(children) : null); + listener.onEvent(event); + } + }; + List childNodes = zkClient.addChildListener(path, zkListener); + listenerMap.put(path, zkListener); + return childNodes; + } } diff --git a/dubbo-metadata/pom.xml b/dubbo-metadata/pom.xml index 17ceada829f..e72db49b9e3 100644 --- a/dubbo-metadata/pom.xml +++ b/dubbo-metadata/pom.xml @@ -31,6 +31,7 @@ dubbo-metadata-api dubbo-metadata-definition-protobuf dubbo-metadata-report-zookeeper + dubbo-metadata-report-failover dubbo-metadata-report-redis dubbo-metadata-report-consul dubbo-metadata-report-etcd diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java index 7b107cbfc2c..4f33ec62afc 100644 --- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java +++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java @@ -56,6 +56,7 @@ public class MonitorFilter implements Filter, Filter.Listener { private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class); private static final String MONITOR_FILTER_START_TIME = "monitor_filter_start_time"; + private static final String MONITOR_REMOTE_HOST_STORE = "monitor_remote_host_store"; /** * The Concurrent counter @@ -84,6 +85,7 @@ public void setMonitorFactory(MonitorFactory monitorFactory) { public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (invoker.getUrl().hasParameter(MONITOR_KEY)) { invocation.put(MONITOR_FILTER_START_TIME, System.currentTimeMillis()); + invocation.put(MONITOR_REMOTE_HOST_STORE, RpcContext.getContext().getRemoteHost()); getConcurrent(invoker, invocation).incrementAndGet(); // count up } return invoker.invoke(invocation); // proceed invocation chain @@ -98,7 +100,7 @@ private AtomicInteger getConcurrent(Invoker invoker, Invocation invocation) { @Override public void onResponse(Result result, Invoker invoker, Invocation invocation) { if (invoker.getUrl().hasParameter(MONITOR_KEY)) { - collect(invoker, invocation, result, RpcContext.getContext().getRemoteHost(), (long) invocation.get(MONITOR_FILTER_START_TIME), false); + collect(invoker, invocation, result, (String) invocation.get(MONITOR_REMOTE_HOST_STORE), (long) invocation.get(MONITOR_FILTER_START_TIME), false); getConcurrent(invoker, invocation).decrementAndGet(); // count down } } @@ -106,7 +108,7 @@ public void onResponse(Result result, Invoker invoker, Invocation invocation) @Override public void onError(Throwable t, Invoker invoker, Invocation invocation) { if (invoker.getUrl().hasParameter(MONITOR_KEY)) { - collect(invoker, invocation, null, RpcContext.getContext().getRemoteHost(), (long) invocation.get(MONITOR_FILTER_START_TIME), true); + collect(invoker, invocation, null, (String) invocation.get(MONITOR_REMOTE_HOST_STORE), (long) invocation.get(MONITOR_FILTER_START_TIME), true); getConcurrent(invoker, invocation).decrementAndGet(); // count down } } diff --git a/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java b/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java index 058325970d6..a6448767d6a 100644 --- a/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java +++ b/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java @@ -48,6 +48,7 @@ import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; +import static org.apache.dubbo.common.constants.CommonConstants.METRICS_PORT; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; @@ -67,6 +68,15 @@ public class MetricsFilterTest { private Invoker serviceInvoker; + private static final String host; + private static final Integer port; + private final URL url = URL.valueOf("dubbo://" + host + ":" + port + "/org.apache.dubbo.monitor.dubbo.service.DemoService?" + METRICS_PORT + "=" + port); + + static { + host = NetUtils.getLocalHost(); + port = NetUtils.getAvailablePort(); + } + @BeforeEach void setUp() { serviceInvoker = mock(Invoker.class); @@ -79,7 +89,7 @@ void setUp() { } private URL getUrl() { - return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/org.apache.dubbo.monitor.dubbo.service.DemoService"); + return url; } private void onInvokeReturns(AppResponse response) { @@ -93,7 +103,7 @@ public Class getInterface() { } public URL getUrl() { - return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880/org.apache.dubbo.monitor.dubbo.service.DemoService"); + return url; } @Override @@ -117,10 +127,10 @@ public void testConsumerSuccess() throws Exception { metricManager.clear(); MetricsFilter metricsFilter = new MetricsFilter(); Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class[]{Integer.class}, new Object[0]); - RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345); + RpcContext.getContext().setRemoteAddress(host, url.getPort()).setLocalAddress(host, NetUtils.getAvailablePort()); RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, CONSUMER_SIDE)); AppResponse response = AppResponseBuilder.create() - .build(); + .build(); onInvokeReturns(response); for (int i = 0; i < 100; i++) { metricsFilter.invoke(serviceInvoker, invocation); @@ -145,11 +155,11 @@ public void testConsumerTimeout() { metricManager.clear(); MetricsFilter metricsFilter = new MetricsFilter(); Invocation invocation = new RpcInvocation("timeoutException", DemoService.class.getName(), "", null, null); - RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345); + RpcContext.getContext().setRemoteAddress(host, url.getPort()).setLocalAddress(host, NetUtils.getAvailablePort()); RpcContext.getContext().setUrl(timeoutInvoker.getUrl().addParameter(SIDE_KEY, CONSUMER_SIDE) - .addParameter(TIMEOUT_KEY, 300)); + .addParameter(TIMEOUT_KEY, 300)); AppResponse response = AppResponseBuilder.create() - .build(); + .build(); onInvokeReturns(response); for (int i = 0; i < 10; i++) { try { @@ -178,10 +188,10 @@ public void testProviderSuccess() throws Exception { metricManager.clear(); MetricsFilter metricsFilter = new MetricsFilter(); Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class[0], new Object[0]); - RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345); + RpcContext.getContext().setRemoteAddress(host, url.getPort()).setLocalAddress(host, NetUtils.getAvailablePort()); RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, PROVIDER)); AppResponse response = AppResponseBuilder.create() - .build(); + .build(); onInvokeReturns(response); for (int i = 0; i < 100; i++) { metricsFilter.invoke(serviceInvoker, invocation); @@ -205,11 +215,11 @@ public void testInvokeMetricsService() { metricManager.clear(); MetricsFilter metricsFilter = new MetricsFilter(); Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class[0], new Object[0]); - RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345); + RpcContext.getContext().setRemoteAddress(host, url.getPort()).setLocalAddress(host, NetUtils.getAvailablePort()); RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, PROVIDER_SIDE) - .addParameter(TIMEOUT_KEY, 300)); + .addParameter(TIMEOUT_KEY, 300)); AppResponse response = AppResponseBuilder.create() - .build(); + .build(); onInvokeReturns(response); for (int i = 0; i < 50; i++) { try { @@ -220,8 +230,8 @@ public void testInvokeMetricsService() { } } Protocol protocol = new DubboProtocol(); - URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":20880/" + MetricsService.class.getName()); - Invoker invoker = protocol.refer(MetricsService.class, url); + URL metricUrl = URL.valueOf("dubbo://" + url.getHost() + ":" + url.getPort() + "/" + MetricsService.class.getName() + "?" + METRICS_PORT + "=" + port); + Invoker invoker = protocol.refer(MetricsService.class, metricUrl); invocation = new RpcInvocation("getMetricsByGroup", DemoService.class.getName(), "", new Class[]{String.class}, new Object[]{DUBBO_GROUP}); try { Thread.sleep(5000); @@ -253,7 +263,7 @@ public void testInvokeMetricsMethodService() { MetricsFilter metricsFilter = new MetricsFilter(); Invocation sayNameInvocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class[0], new Object[0]); Invocation echoInvocation = new RpcInvocation("echo", DemoService.class.getName(), "", new Class[]{Integer.class}, new Integer[]{1}); - RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345); + RpcContext.getContext().setRemoteAddress(host, url.getPort()).setLocalAddress(host, NetUtils.getAvailablePort()); RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, PROVIDER_SIDE) .addParameter(TIMEOUT_KEY, 300)); AppResponse response = AppResponseBuilder.create() @@ -275,8 +285,8 @@ public void testInvokeMetricsMethodService() { } Protocol protocol = new DubboProtocol(); - URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":20880/" + MetricsService.class.getName()); - Invoker invoker = protocol.refer(MetricsService.class, url); + URL metricUrl = URL.valueOf("dubbo://" + url.getHost() + ":" + url.getPort() + "/" + MetricsService.class.getName() + "?" + METRICS_PORT + "=" + port); + Invoker invoker = protocol.refer(MetricsService.class, metricUrl); Invocation invocation = new RpcInvocation("getMetricsByGroup", DemoService.class.getName(), "", new Class[]{String.class}, new Object[]{DUBBO_GROUP}); try { Thread.sleep(15000); @@ -301,23 +311,23 @@ public void testInvokeMetricsMethodService() { } Assertions.assertEquals(50.0, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("success_bucket_count")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("success_bucket_count")); Assertions.assertEquals(50.0, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("success_bucket_count")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("success_bucket_count")); Assertions.assertEquals(50.0, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("timeoutError_bucket_count")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("timeoutError_bucket_count")); Assertions.assertEquals(50.0, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("timeoutError_bucket_count")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("timeoutError_bucket_count")); Assertions.assertEquals(100.0 / 15, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("qps")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("qps")); Assertions.assertEquals(100.0 / 15, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("qps")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("qps")); Assertions.assertEquals(50.0 / 100.0, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("success_rate")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void sayName()").get("success_rate")); Assertions.assertEquals(50.0 / 100.0, - methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("success_rate")); + methodMetricMap.get("org.apache.dubbo.monitor.dubbo.service.DemoService.void echo(Integer)").get("success_rate")); } } diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java index 06457f6bd8b..2c6b4226a37 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java @@ -16,17 +16,80 @@ */ package org.apache.dubbo.qos.command.impl; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.config.utils.ConfigValidationUtils; import org.apache.dubbo.qos.command.BaseCommand; import org.apache.dubbo.qos.command.CommandContext; import org.apache.dubbo.qos.command.annotation.Cmd; +import org.apache.dubbo.qos.textui.TTable; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ProviderModel; -@Cmd(name = "start",summary = "Judge if service has started? ") +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Cmd(name = "ready",summary = "Judge if application or service has started? ") public class Ready implements BaseCommand { @Override public String execute(CommandContext commandContext, String[] args) { - return DubboBootstrap.getInstance().isReady() ? "true" : "false"; + String serviceName = args.length > 0 ? args[0] : null; + if (StringUtils.isEmpty(serviceName)) { + // judge application has started + return DubboBootstrap.getInstance().isReady() ? "true" : "false"; + } else { + // judge service has started + Map serviceReadyMap = isServiceReady(serviceName); + if (serviceReadyMap == null || serviceReadyMap.size() <= 0) { + return "can't match service=" + serviceName; + } + return buildUiText(serviceReadyMap); + } + } + + private String buildUiText(Map serviceReadyMap) { + TTable tTable = new TTable(new TTable.ColumnDefine[]{ + new TTable.ColumnDefine(TTable.Align.MIDDLE), + new TTable.ColumnDefine(TTable.Align.MIDDLE) + }); + + //Header + tTable.addRow("Provider Service Name", "STATUS"); + for (Map.Entry entry : serviceReadyMap.entrySet()) { + String status = Boolean.TRUE.equals(entry.getValue()) ? "TRUE" : "FALSE"; + tTable.addRow(entry.getKey(),status); + } + return tTable.rendering(); + } + + /** + * judge service provider is started + * @param serviceName service name,eg: org.apache.dubbo.demo.DemoService + * @return Map[serviceKey,isStarted] eg:[org.apache.dubbo.demo.DemoService,true] or [group1/org.apache.dubbo.demo.DemoService,false] + */ + private Map isServiceReady(String serviceName) { + Map res = new HashMap<>(); + for (ProviderModel providerModel : ApplicationModel.allProviderModels()) { + String serviceKey = providerModel.getServiceKey(); + String interfaceName = providerModel.getServiceConfig().getInterface(); + if (interfaceName.equals(serviceName)) { + List needRegistryURLs = ConfigValidationUtils.loadRegistries(providerModel.getServiceConfig(), true); + List registeredRegistryURLs = providerModel.getStatedUrl().stream() + .filter(x -> Boolean.TRUE.equals(x.isRegistered())) + .map(ProviderModel.RegisterStatedURL::getRegistryUrl) + .collect(Collectors.toList()); + if (needRegistryURLs.size() == registeredRegistryURLs.size()) { + res.put(serviceKey,true); + } else { + res.put(serviceKey,false); + } + } + } + return res; } } diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java index 3da6cf4aa6e..02cd3b21c4c 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/ListTelnetHandler.java @@ -112,6 +112,7 @@ private void printAllReferredServices(StringBuilder buf, boolean detail) { buf.append(" addresses: "); buf.append(ServiceCheckUtils.getConsumerAddressNum(consumer)); } + buf.append("\r\n"); } } diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java index c77c86c3824..3cbfc8319fc 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java @@ -44,7 +44,7 @@ public class LogTelnetHandler implements TelnetHandler { public String telnet(Channel channel, String message) { long size; File file = LoggerFactory.getFile(); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); if (message == null || message.trim().length() == 0) { buf.append("EXAMPLE: log error / log 100"); } else { diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java index 245b820d7e6..7d8a9983833 100644 --- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java @@ -90,6 +90,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t p.addLast(new StringDecoder(CharsetUtil.UTF_8)); p.addLast(new StringEncoder(CharsetUtil.UTF_8)); p.addLast(new IdleStateHandler(0, 0, 5 * 60)); + p.addLast(new TelnetIdleEventHandler()); p.addLast(new TelnetProcessHandler()); p.remove(this); } diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.java new file mode 100644 index 00000000000..732df24e295 --- /dev/null +++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetIdleEventHandler.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.qos.server.handler; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.timeout.IdleStateEvent; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; + +public class TelnetIdleEventHandler extends ChannelDuplexHandler { + private static final Logger log = LoggerFactory.getLogger(TelnetIdleEventHandler.class); + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + // server will close channel when server don't receive any request from client util timeout. + if (evt instanceof IdleStateEvent) { + Channel channel = ctx.channel(); + log.info("IdleStateEvent triggered, close channel " + channel); + channel.close(); + } else { + super.userEventTriggered(ctx, evt); + } + } + +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java index bebc73ad08d..6cdcc9f063d 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java @@ -16,6 +16,9 @@ */ package org.apache.dubbo.registry.client; +import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.isInstanceUpdated; +import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.resetInstanceUpdateKey; + public abstract class AbstractServiceDiscovery implements ServiceDiscovery { protected ServiceInstance serviceInstance; @@ -26,11 +29,28 @@ public ServiceInstance getLocalInstance() { } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { + public final void register(ServiceInstance serviceInstance) throws RuntimeException { + this.serviceInstance = serviceInstance; + doRegister(serviceInstance); } + /** + * It should be implement in kinds of service discovers. + */ + public abstract void doRegister(ServiceInstance serviceInstance); + @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { + public final void update(ServiceInstance serviceInstance) throws RuntimeException { + if (!isInstanceUpdated(serviceInstance)) { + return; + } this.serviceInstance = serviceInstance; + doUpdate(serviceInstance); + resetInstanceUpdateKey(serviceInstance); } + + /** + * It should be implement in kinds of service discovers. + */ + public abstract void doUpdate(ServiceInstance serviceInstance); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java index 42cc10b8f0e..56fa41fe482 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java @@ -18,12 +18,13 @@ import org.apache.dubbo.common.URL; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; public class DefaultRegistryClusterIdentifier implements RegistryClusterIdentifier { @Override public String providerKey(URL url) { - return url.getParameter(REGISTRY_CLUSTER_KEY); + return url.getParameter(REGISTRY_CLUSTER_KEY, DEFAULT_KEY); } @Override diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java index eb581f945cf..2cd28ea1829 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java @@ -174,8 +174,12 @@ public InstanceAddressURL toURL() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof DefaultServiceInstance)) return false; + if (this == o) { + return true; + } + if (!(o instanceof DefaultServiceInstance)) { + return false; + } DefaultServiceInstance that = (DefaultServiceInstance) o; boolean equals = Objects.equals(getServiceName(), that.getServiceName()) && Objects.equals(getHost(), that.getHost()) && @@ -184,7 +188,7 @@ public boolean equals(Object o) { if (entry.getKey().equals(REVISION_KEY)) { continue; } - equals = equals && !entry.getValue().equals(that.getMetadata().get(entry.getKey())); + equals = equals && entry.getValue().equals(that.getMetadata().get(entry.getKey())); } return equals; diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java index 2a5116870fe..7a0e330930e 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java @@ -54,7 +54,7 @@ * @see FileSystemDynamicConfiguration * @since 2.7.5 */ -public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListener { +public class FileSystemServiceDiscovery extends AbstractServiceDiscovery implements EventListener { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -62,8 +62,6 @@ public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListen private FileSystemDynamicConfiguration dynamicConfiguration; - private ServiceInstance serviceInstance; - @Override public void onEvent(ServiceInstancesChangedEvent event) { @@ -136,13 +134,7 @@ public URL getUrl() { } @Override - public ServiceInstance getLocalInstance() { - return serviceInstance; - } - - @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; + public void doRegister(ServiceInstance serviceInstance) { String serviceInstanceId = getServiceInstanceId(serviceInstance); String serviceName = getServiceName(serviceInstance); String content = toJSONString(serviceInstance); @@ -174,8 +166,9 @@ private void lockFile(String serviceInstanceId, String serviceName) { }); } + @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { + public void doUpdate(ServiceInstance serviceInstance) { register(serviceInstance); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java index 5159cfc2985..494785db95f 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java @@ -27,6 +27,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; public class InstanceAddressURL extends URL { @@ -105,6 +106,8 @@ public String getParameter(String key) { return getGroup(); } else if (INTERFACE_KEY.equals(key)) { return getServiceInterface(); + } else if (REMOTE_APPLICATION_KEY.equals(key)) { + return instance.getServiceName(); } String protocolServiceKey = getProtocolServiceKey(); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java index 9800c35fcd7..9d08e4032be 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java @@ -86,7 +86,7 @@ public interface ServiceDiscovery extends Prioritized { /** * Unregisters an instance of {@link ServiceInstance}. * - * @param serviceInstance an instance of {@link ServiceInstance} to be deregistered + * @param serviceInstance an instance of {@link ServiceInstance} to be unregistered * @throws RuntimeException if failed */ void unregister(ServiceInstance serviceInstance) throws RuntimeException; @@ -116,7 +116,7 @@ default int getDefaultPageSize() { * * @param serviceName the service name * @return non-null {@link List} - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null */ default List getInstances(String serviceName) throws NullPointerException { @@ -147,7 +147,7 @@ default List getInstances(String serviceName) throws NullPointe * @param offset the offset of request , the number "0" indicates first page * @param pageSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited * @return non-null {@link Page} object - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null * @throws IllegalArgumentException if offset or pageSize is negative number * @throws UnsupportedOperationException if not supported */ @@ -165,7 +165,7 @@ default Page getInstances(String serviceName, int offset, int p * @param pageSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited * @param healthyOnly if true , filter healthy instances only * @return non-null {@link Page} object - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null * @throws IllegalArgumentException if offset or pageSize is negative number * @throws UnsupportedOperationException if not supported */ @@ -182,7 +182,7 @@ default Page getInstances(String serviceName, int offset, int p * @param requestSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited * @return non-null read-only {@link Map} whose key is the service name and value is * the {@link Page pagination} of {@link ServiceInstance service instances} - * @throws NullPointerException if serviceName is null is null + * @throws NullPointerException if serviceName is null * @throws IllegalArgumentException if offset or requestSize is negative number * @throws UnsupportedOperationException if not supported */ @@ -198,7 +198,7 @@ default Map> getInstances(Iterable service /** * Add an instance of {@link ServiceInstancesChangedListener} for specified service *

    - * Default, Current method will be invoked by {@link ServiceDiscoveryRegistry#subscribe(URL, NotifyListener) + * Default, current method will be invoked by {@link ServiceDiscoveryRegistry#subscribe(URL, NotifyListener) * the ServiceDiscoveryRegistry on the subscription}, and it's mandatory to * {@link EventDispatcher#addEventListener(EventListener) add} the {@link ServiceInstancesChangedListener} argument * into {@link EventDispatcher} whether the subclass implements same approach or not, thus this method is used to @@ -216,6 +216,15 @@ default void addServiceInstancesChangedListener(ServiceInstancesChangedListener throws NullPointerException, IllegalArgumentException { } + /** + * unsubscribe to instances change event. + * @param listener + * @throws IllegalArgumentException + */ + default void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) + throws IllegalArgumentException { + } + /** * Dispatch the {@link ServiceInstancesChangedEvent} * diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java index a3505902e09..ac0b5261a20 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java @@ -111,6 +111,7 @@ public class ServiceDiscoveryRegistry implements Registry { /* apps - listener */ private final Map serviceListeners = new HashMap<>(); + private final Map serviceToAppsMapping = new HashMap<>(); private URL registryURL; @@ -259,8 +260,10 @@ public void doSubscribe(URL url, NotifyListener listener) { writableMetadataService.subscribeURL(url); Set serviceNames = getServices(url, listener); + if (CollectionUtils.isEmpty(serviceNames)) { - throw new IllegalStateException("Should has at least one way to know which services this interface belongs to, subscription url: " + url); + logger.warn("Should has at least one way to know which services this interface belongs to, subscription url: " + url); + return; } subscribeURLs(url, listener, serviceNames); @@ -280,6 +283,10 @@ public final void unsubscribe(URL url, NotifyListener listener) { public void doUnsubscribe(URL url, NotifyListener listener) { writableMetadataService.unsubscribeURL(url); + String protocolServiceKey = url.getServiceKey() + GROUP_CHAR_SEPARATOR + url.getParameter(PROTOCOL_KEY, DUBBO); + String serviceNamesKey = serviceToAppsMapping.remove(protocolServiceKey); + ServiceInstancesChangedListener instancesChangedListener = serviceListeners.get(serviceNamesKey); + instancesChangedListener.removeListener(protocolServiceKey); } @Override @@ -308,22 +315,30 @@ public void destroy() { protected void subscribeURLs(URL url, NotifyListener listener, Set serviceNames) { String serviceNamesKey = serviceNames.toString(); + String protocolServiceKey = url.getServiceKey() + GROUP_CHAR_SEPARATOR + url.getParameter(PROTOCOL_KEY, DUBBO); + serviceToAppsMapping.put(protocolServiceKey, serviceNamesKey); + // register ServiceInstancesChangedListener ServiceInstancesChangedListener serviceListener = serviceListeners.computeIfAbsent(serviceNamesKey, k -> new ServiceInstancesChangedListener(serviceNames, serviceDiscovery)); serviceListener.setUrl(url); listener.addServiceListener(serviceListener); - String protocolServiceKey = url.getServiceKey() + GROUP_CHAR_SEPARATOR + url.getParameter(PROTOCOL_KEY, DUBBO); serviceListener.addListener(protocolServiceKey, listener); registerServiceInstancesChangedListener(url, serviceListener); + // FIXME: This will cause redundant duplicate notifications serviceNames.forEach(serviceName -> { List serviceInstances = serviceDiscovery.getInstances(serviceName); - serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances)); + if (CollectionUtils.isNotEmpty(serviceInstances)) { + serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances)); + } else { + logger.info("getInstances by serviceName=" + serviceName + " is empty, waiting for serviceListener callback. url=" + url); + } }); listener.notify(serviceListener.getUrls(protocolServiceKey)); + } /** @@ -356,17 +371,16 @@ protected Set getServices(URL subscribedURL, final NotifyListener listen String serviceNames = subscribedURL.getParameter(PROVIDED_BY); if (StringUtils.isNotEmpty(serviceNames)) { - subscribedServices.addAll(parseServices(serviceNames)); - } - - serviceNames = subscribedURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY); - if (StringUtils.isNotEmpty(serviceNames)) { + logger.info(subscribedURL.getServiceInterface() + " mapping to " + serviceNames + " instructed by provided-by set by user."); subscribedServices.addAll(parseServices(serviceNames)); } if (isEmpty(subscribedServices)) { - subscribedServices.addAll(findMappedServices(subscribedURL, new DefaultMappingListener(subscribedURL, subscribedServices, listener))); + Set mappedServices = findMappedServices(subscribedURL, new DefaultMappingListener(subscribedURL, subscribedServices, listener)); + logger.info(subscribedURL.getServiceInterface() + " mapping to " + serviceNames + " instructed by remote metadata center."); + subscribedServices.addAll(mappedServices); if (isEmpty(subscribedServices)) { + logger.info(subscribedURL.getServiceInterface() + " mapping to " + serviceNames + " by default."); subscribedServices.addAll(getSubscribedServices()); } } @@ -481,4 +495,4 @@ public void onEvent(MappingChangedEvent event) { } } } -} \ No newline at end of file +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java index 15c187f5039..df573d1c736 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java @@ -127,6 +127,9 @@ private void refreshInvoker(List invokerUrls) { logger.warn("destroyUnusedInvokers error. ", e); } } + + // notify invokers refreshed + this.invokersChanged(); } /** @@ -199,7 +202,8 @@ private List> toMergeInvokerList(List> invokers) { /** * Close all invokers */ - private void destroyAllInvokers() { + @Override + protected void destroyAllInvokers() { Map> localUrlInvokerMap = this.urlInvokerMap; // local reference if (localUrlInvokerMap != null) { for (Invoker invoker : new ArrayList<>(localUrlInvokerMap.values())) { @@ -258,34 +262,4 @@ private void destroyUnusedInvokers(Map> oldUrlInvokerMap, Map } } } - - @Override - public void destroy() { - if (isDestroyed()) { - return; - } - - // unregister. - try { - if (getRegisteredConsumerUrl() != null && registry != null && registry.isAvailable()) { - registry.unregister(getRegisteredConsumerUrl()); - } - } catch (Throwable t) { - logger.warn("unexpected error when unregister service " + serviceKey + "from registry" + registry.getUrl(), t); - } - // unsubscribe. - try { - if (getConsumerUrl() != null && registry != null && registry.isAvailable()) { - registry.unsubscribe(getConsumerUrl(), this); - } - } catch (Throwable t) { - logger.warn("unexpected error when unsubscribe service " + serviceKey + "from registry" + registry.getUrl(), t); - } - super.destroy(); // must be executed after unsubscribing - try { - destroyAllInvokers(); - } catch (Throwable t) { - logger.warn("Failed to destroy service " + serviceKey, t); - } - } } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListener.java index 91fb07d7149..8aad92a56f3 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListener.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListener.java @@ -58,11 +58,11 @@ public void onEvent(ServiceInstanceRegisteredEvent event) { } public void onEvent(ServiceInstancesChangedEvent event) { - info("The services'[name : %s] instances[size : %s] has been changed.", event.getServiceName(), event.getServiceInstances().size()); + info("The service[name : %s] instances[size : %s] has been changed.", event.getServiceName(), event.getServiceInstances().size()); } public void onEvent(ServiceInstancePreUnregisteredEvent event) { - info("%s is registering from %s...", event.getServiceInstance(), event.getSource()); + info("%s is unregistering from %s...", event.getServiceInstance(), event.getSource()); } public void onEvent(ServiceInstanceUnregisteredEvent event) { diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java index e99b40cd3d3..d40fd013973 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -61,8 +62,9 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener private final Set serviceNames; private final ServiceDiscovery serviceDiscovery; + private final String registryId; private URL url; - private Map listeners; + private Map> listeners; private Map> allInstances; @@ -73,6 +75,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener public ServiceInstancesChangedListener(Set serviceNames, ServiceDiscovery serviceDiscovery) { this.serviceNames = serviceNames; this.serviceDiscovery = serviceDiscovery; + this.registryId = serviceDiscovery.getUrl().getParameter("id"); this.listeners = new HashMap<>(); this.allInstances = new HashMap<>(); this.serviceUrls = new HashMap<>(); @@ -88,10 +91,14 @@ public synchronized void onEvent(ServiceInstancesChangedEvent event) { logger.info("Received instance notification, serviceName: " + event.getServiceName() + ", instances: " + event.getServiceInstances().size()); String appName = event.getServiceName(); allInstances.put(appName, event.getServiceInstances()); + if (logger.isDebugEnabled()) { + logger.debug(event.getServiceInstances().toString()); + } Map> revisionToInstances = new HashMap<>(); Map> localServiceToRevisions = new HashMap<>(); Map, List> revisionsToUrls = new HashMap(); + Map> tmpServiceUrls = new HashMap<>(); for (Map.Entry> entry : allInstances.entrySet()) { List instances = entry.getValue(); for (ServiceInstance instance : instances) { @@ -108,7 +115,7 @@ public synchronized void onEvent(ServiceInstancesChangedEvent event) { metadata = getMetadataInfo(instance); logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata); if (metadata != null) { - revisionToMetadata.put(revision, getMetadataInfo(instance)); + revisionToMetadata.put(revision, metadata); } else { } @@ -123,25 +130,26 @@ public synchronized void onEvent(ServiceInstancesChangedEvent event) { // Set set = localServiceToRevisions.computeIfAbsent(url.getServiceKey(), k -> new TreeSet<>()); // set.add(revision); // } + } - localServiceToRevisions.forEach((serviceKey, revisions) -> { - List urls = revisionsToUrls.get(revisions); - if (urls != null) { - serviceUrls.put(serviceKey, urls); - } else { - urls = new ArrayList<>(); - for (String r : revisions) { - for (ServiceInstance i : revisionToInstances.get(r)) { - urls.add(i.toURL()); - } + localServiceToRevisions.forEach((serviceKey, revisions) -> { + List urls = revisionsToUrls.get(revisions); + if (urls != null) { + tmpServiceUrls.put(serviceKey, urls); + } else { + urls = new ArrayList<>(); + for (String r : revisions) { + for (ServiceInstance i : revisionToInstances.get(r)) { + urls.add(i.toURL()); } - revisionsToUrls.put(revisions, urls); - serviceUrls.put(serviceKey, urls); } - }); - } + revisionsToUrls.put(revisions, urls); + tmpServiceUrls.put(serviceKey, urls); + } + }); } + this.serviceUrls = tmpServiceUrls; this.notifyAddressChanged(); } @@ -161,6 +169,9 @@ private MetadataInfo getMetadataInfo(ServiceInstance instance) { instance.getExtendParams().putIfAbsent(REGISTRY_CLUSTER_KEY, RegistryClusterIdentifier.getExtension(url).consumerKey(url)); MetadataInfo metadataInfo; try { + if (logger.isDebugEnabled()) { + logger.info("Instance " + instance.getAddress() + " is using metadata type " + metadataType); + } if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) { RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService(); metadataInfo = remoteMetadataService.getMetadata(instance); @@ -168,8 +179,11 @@ private MetadataInfo getMetadataInfo(ServiceInstance instance) { MetadataService metadataServiceProxy = MetadataUtils.getMetadataServiceProxy(instance, serviceDiscovery); metadataInfo = metadataServiceProxy.getMetadataInfo(ServiceInstanceMetadataUtils.getExportedServicesRevision(instance)); } + if (logger.isDebugEnabled()) { + logger.info("Metadata " + metadataInfo.toString()); + } } catch (Exception e) { - logger.error("Failed to load service metadata, metadta type is " + metadataType, e); + logger.error("Failed to load service metadata, metadata type is " + metadataType, e); metadataInfo = null; // TODO, load metadata backup. Stop getting metadata after x times of failure for one revision? } @@ -177,9 +191,10 @@ private MetadataInfo getMetadataInfo(ServiceInstance instance) { } private void notifyAddressChanged() { - listeners.forEach((key, notifyListener) -> { - //FIXME, group wildcard match - notifyListener.notify(toUrlsWithEmpty(serviceUrls.get(key))); + listeners.forEach((key, notifyListeners) -> { + notifyListeners.forEach(notifyListener -> { + notifyListener.notify(toUrlsWithEmpty(serviceUrls.get(key))); + }); }); } @@ -191,7 +206,14 @@ private List toUrlsWithEmpty(List urls) { } public void addListener(String serviceKey, NotifyListener listener) { - this.listeners.put(serviceKey, listener); + this.listeners.computeIfAbsent(serviceKey, k -> new HashSet<>()).add(listener); + } + + public void removeListener(String serviceKey) { + listeners.remove(serviceKey); + if (listeners.isEmpty()) { + serviceDiscovery.removeServiceInstancesChangedListener(this); + } } public List getUrls(String serviceKey) { @@ -223,16 +245,24 @@ public final boolean accept(ServiceInstancesChangedEvent event) { return serviceNames.contains(event.getServiceName()); } + public String getRegistryId() { + return registryId; + } + @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ServiceInstancesChangedListener)) return false; + if (this == o) { + return true; + } + if (!(o instanceof ServiceInstancesChangedListener)) { + return false; + } ServiceInstancesChangedListener that = (ServiceInstancesChangedListener) o; - return Objects.equals(getServiceNames(), that.getServiceNames()); + return Objects.equals(getServiceNames(), that.getServiceNames()) && Objects.equals(getRegistryId(), that.getRegistryId()); } @Override public int hashCode() { - return Objects.hash(getClass(), getServiceNames()); + return Objects.hash(getClass(), getServiceNames(), getRegistryId()); } } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java index b6d7a9c98ba..4892b618eb1 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java @@ -30,7 +30,6 @@ import java.util.Set; import static java.util.Arrays.asList; -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.model.ApplicationModel.getName; @@ -77,9 +76,7 @@ public Set getAndListen(URL url, MappingListener mappingListener) { protected String getRegistryCluster(URL url) { String registryCluster = RegistryClusterIdentifier.getExtension(url).providerKey(url); - if (registryCluster == null) { - registryCluster = DEFAULT_KEY; - } + int i = registryCluster.indexOf(","); if (i > 0) { registryCluster = registryCluster.substring(0, i); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLBuilder.java index 7f77bdcfc9f..8d1aa8aabbc 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLBuilder.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLBuilder.java @@ -34,7 +34,7 @@ public interface MetadataServiceURLBuilder { * Build the {@link URL URLs} from the specified {@link ServiceInstance} * * @param serviceInstance {@link ServiceInstance} - * @return TODO, usually, we generate one metadata url from one instance. There's no scenario to return a metadta url list. + * @return TODO, usually, we generate one metadata url from one instance. There's no scenario to return a metadata url list. */ List build(ServiceInstance serviceInstance); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java index 4684a27bcc0..0a6a10602ed 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java @@ -71,7 +71,7 @@ public static void publishServiceDefinition(URL url) { } public static MetadataService getMetadataServiceProxy(ServiceInstance instance, ServiceDiscovery serviceDiscovery) { - String key = instance.getServiceName() + "##" + instance.getId() + "##" + + String key = instance.getServiceName() + "##" + ServiceInstanceMetadataUtils.getExportedServicesRevision(instance); return metadataServiceProxies.computeIfAbsent(key, k -> { MetadataServiceURLBuilder builder = null; @@ -81,10 +81,10 @@ public static MetadataService getMetadataServiceProxy(ServiceInstance instance, Map metadata = instance.getMetadata(); // METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only on instances of spring-cloud-alibaba. String dubboURLsJSON = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME); - if (metadata.isEmpty() || StringUtils.isEmpty(dubboURLsJSON)) { - builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME); - } else { + if (StringUtils.isNotEmpty(dubboURLsJSON)) { builder = loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME); + } else { + builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME); } List urls = builder.build(instance); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java index 95e5349d79c..f4b30f67a1e 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java @@ -260,6 +260,10 @@ public static boolean isInstanceUpdated(ServiceInstance instance) { return "true".equals(instance.getExtendParams().get(INSTANCE_REVISION_UPDATED_KEY)); } + public static void resetInstanceUpdateKey(ServiceInstance instance) { + instance.getExtendParams().remove(INSTANCE_REVISION_UPDATED_KEY); + } + public static void refreshMetadataAndInstance() { RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService(); remoteMetadataService.publishMetadata(ApplicationModel.getName()); diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java index 34dce94284b..c90eea06a0d 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java @@ -19,12 +19,8 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.config.ConfigurationUtils; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.MetadataService; import org.apache.dubbo.registry.client.ServiceInstance; -import org.apache.dubbo.remoting.Constants; -import org.apache.dubbo.rpc.model.ApplicationModel; import java.util.ArrayList; import java.util.List; @@ -32,12 +28,10 @@ import static java.lang.String.valueOf; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; -import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_TIMEOUT_VALUE; import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY; import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataServiceURLsParams; @@ -49,9 +43,7 @@ * @since 2.7.5 */ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuilder { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - + public static final String NAME = "standard"; /** @@ -71,66 +63,27 @@ public List build(ServiceInstance serviceInstance) { String host = serviceInstance.getHost(); - if (paramsMap.isEmpty()) { - // ServiceInstance Metadata is empty. Happened when registry not support metadata write. - urls.add(generateUrlWithoutMetadata(serviceName, host)); - } else { - for (Map.Entry> entry : paramsMap.entrySet()) { - String protocol = entry.getKey(); - Map params = entry.getValue(); - - urls.add(generateWithMetadata(serviceName, host, protocol, params)); - } + for (Map.Entry> entry : paramsMap.entrySet()) { + String protocol = entry.getKey(); + Map params = entry.getValue(); + int port = Integer.parseInt(params.get(PORT_KEY)); + URLBuilder urlBuilder = new URLBuilder() + .setHost(host) + .setPort(port) + .setProtocol(protocol) + .setPath(MetadataService.class.getName()) + .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE)) + .addParameter(SIDE_KEY, CONSUMER); + + // add parameters + params.forEach((name, value) -> urlBuilder.addParameter(name, valueOf(value))); + + // add the default parameters + urlBuilder.addParameter(GROUP_KEY, serviceName); + + urls.add(urlBuilder.build()); } return urls; } - - private URL generateWithMetadata(String serviceName, String host, String protocol, Map params) { - int port = Integer.parseInt(params.get(PORT_KEY)); - URLBuilder urlBuilder = new URLBuilder() - .setHost(host) - .setPort(port) - .setProtocol(protocol) - .setPath(MetadataService.class.getName()) - .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE)) - .addParameter(SIDE_KEY, CONSUMER); - - // add parameters - params.forEach((name, value) -> urlBuilder.addParameter(name, valueOf(value))); - - // add the default parameters - urlBuilder.addParameter(GROUP_KEY, serviceName); - return urlBuilder.build(); - } - - private URL generateUrlWithoutMetadata(String serviceName, String host) { - Integer port = ApplicationModel.getApplicationConfig().getMetadataServicePort(); - - if (port == null || port < 1) { - String message = "Metadata Service Port should be specified for consumer. " + - "Please set dubbo.application.metadataServicePort and " + - "make sure it has been set in provider side. " + - "ServiceName: " + serviceName + " Host: " + host; - - logger.error(message); - throw new IllegalStateException(message); - } - - URLBuilder urlBuilder = new URLBuilder() - .setHost(host) - .setPort(port) - .setProtocol(DUBBO_PROTOCOL) - .setPath(MetadataService.class.getName()) - .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE)) - .addParameter(Constants.RECONNECT_KEY, false) - .addParameter(SIDE_KEY, CONSUMER) - .addParameter(GROUP_KEY, serviceName) - .addParameter(VERSION_KEY, MetadataService.VERSION); - - // add ServiceInstance Metadata notify support - urlBuilder.addParameter("getAndListenServiceDiscoveryMetadata.1.callback", true); - - return urlBuilder.build(); - } } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java index c2403ead6e7..448d73ddb25 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java @@ -20,7 +20,6 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.metadata.MetadataChangeListener; import org.apache.dubbo.metadata.MetadataInfo; import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo; import org.apache.dubbo.metadata.MetadataService; @@ -49,8 +48,10 @@ import static java.util.Collections.emptySortedSet; import static java.util.Collections.unmodifiableSortedSet; import static org.apache.dubbo.common.URL.buildKey; +import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; @@ -77,8 +78,6 @@ public class InMemoryWritableMetadataService implements WritableMetadataService ConcurrentNavigableMap> exportedServiceURLs = new ConcurrentSkipListMap<>(); ConcurrentMap metadataInfos; final Semaphore metadataSemaphore = new Semaphore(1); - String serviceDiscoveryMetadata; - ConcurrentMap metadataChangeListenerMap = new ConcurrentHashMap<>(); // ==================================================================================== // @@ -168,17 +167,21 @@ public boolean unsubscribeURL(URL url) { @Override public void publishServiceDefinition(URL providerUrl) { try { - String interfaceName = providerUrl.getParameter(INTERFACE_KEY); - if (StringUtils.isNotEmpty(interfaceName) - && !ProtocolUtils.isGeneric(providerUrl.getParameter(GENERIC_KEY))) { - Class interfaceClass = Class.forName(interfaceName); - ServiceDefinition serviceDefinition = ServiceDefinitionBuilder.build(interfaceClass); - Gson gson = new Gson(); - String data = gson.toJson(serviceDefinition); - serviceDefinitions.put(providerUrl.getServiceKey(), data); + if (!ProtocolUtils.isGeneric(providerUrl.getParameter(GENERIC_KEY))) { + String interfaceName = providerUrl.getParameter(INTERFACE_KEY); + if (StringUtils.isNotEmpty(interfaceName)) { + Class interfaceClass = Class.forName(interfaceName); + ServiceDefinition serviceDefinition = ServiceDefinitionBuilder.build(interfaceClass); + Gson gson = new Gson(); + String data = gson.toJson(serviceDefinition); + serviceDefinitions.put(providerUrl.getServiceKey(), data); + return; + } + logger.error("publishProvider interfaceName is empty . providerUrl: " + providerUrl.toFullString()); + } else if (CONSUMER_SIDE.equalsIgnoreCase(providerUrl.getParameter(SIDE_KEY))) { + //to avoid consumer generic invoke style error return; } - logger.error("publishProvider interfaceName is empty . providerUrl: " + providerUrl.toFullString()); } catch (ClassNotFoundException e) { //ignore error logger.error("publishProvider getServiceDescriptor error. providerUrl: " + providerUrl.toFullString(), e); @@ -209,27 +212,11 @@ public MetadataInfo getMetadataInfo(String revision) { return null; } - @Override - public void exportServiceDiscoveryMetadata(String metadata) { - this.serviceDiscoveryMetadata = metadata; - } - - @Override - public Map getMetadataChangeListenerMap() { - return metadataChangeListenerMap; - } - - @Override - public String getAndListenServiceDiscoveryMetadata(String consumerId, MetadataChangeListener listener) { - metadataChangeListenerMap.put(consumerId, listener); - return serviceDiscoveryMetadata; - } - public void blockUntilUpdated() { try { metadataSemaphore.acquire(); } catch (InterruptedException e) { - logger.warn("metadata refresh thread has been interrupted unexpectedly while wating for update.", e); + logger.warn("metadata refresh thread has been interrupted unexpectedly while waiting for update.", e); } } @@ -310,4 +297,5 @@ public int compare(URL o1, URL o2) { return o1.toFullString().compareTo(o2.toFullString()); } } + } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java index 01c4effe634..209a9fab9e0 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java @@ -69,6 +69,10 @@ public void publishMetadata(String serviceName) { if (metadataReport == null) { metadataReport = getMetadataReports().entrySet().iterator().next().getValue(); } + logger.info("Publishing metadata to " + metadataReport.getClass().getSimpleName()); + if (logger.isDebugEnabled()) { + logger.debug(metadataInfo.toString()); + } metadataReport.publishAppMetadata(identifier, metadataInfo); metadataInfo.markReported(); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java new file mode 100644 index 00000000000..2936688245e --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.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.dubbo.registry.client.migration; + +import org.apache.dubbo.common.config.ConfigurationUtils; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; + +import java.util.List; + +public class DefaultMigrationAddressComparator implements MigrationAddressComparator { + private static final Logger logger = LoggerFactory.getLogger(DefaultMigrationAddressComparator.class); + private static final String MIGRATION_THRESHOLD = "dubbo.application.migration.threshold"; + private static final String DEFAULT_THRESHOLD_STRING = "0.8"; + private static final float DEFAULT_THREAD = 0.8f; + + @Override + public boolean shouldMigrate(ClusterInvoker serviceDiscoveryInvoker, ClusterInvoker invoker) { + if (!serviceDiscoveryInvoker.isAvailable()) { + logger.info("No instance address available, will not migrate."); + return false; + } + if (!invoker.isAvailable()) { + logger.info("No interface address available, will migrate."); + return true; + } + + List> invokers1 = serviceDiscoveryInvoker.getDirectory().getAllInvokers(); + List> invokers2 = invoker.getDirectory().getAllInvokers(); + + int newAddressSize = CollectionUtils.isNotEmpty(invokers1) ? invokers1.size() : 0; + int oldAddressSize = CollectionUtils.isNotEmpty(invokers2) ? invokers2.size() : 0; + + String rawThreshold = ConfigurationUtils.getDynamicProperty(MIGRATION_THRESHOLD, DEFAULT_THRESHOLD_STRING); + float threshold; + try { + threshold = Float.parseFloat(rawThreshold); + } catch (Exception e) { + logger.error("Invalid migration threshold " + rawThreshold); + threshold = DEFAULT_THREAD; + } + + logger.info("Instance address size " + newAddressSize + ", interface address size " + oldAddressSize + ", threshold " + threshold); + + if (newAddressSize != 0 && oldAddressSize == 0) { + return true; + } + if (newAddressSize == 0 && oldAddressSize == 0) { + return false; + } + + if (((float)newAddressSize / (float)oldAddressSize) >= threshold) { + return true; + } + return false; + } +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/InvokersChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/InvokersChangedListener.java new file mode 100644 index 00000000000..74cd94787a7 --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/InvokersChangedListener.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.dubbo.registry.client.migration; + +public interface InvokersChangedListener { + void onChange(); +} \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationAddressComparator.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationAddressComparator.java new file mode 100644 index 00000000000..2be527b34bc --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationAddressComparator.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.client.migration; + +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; + +@SPI +public interface MigrationAddressComparator { + boolean shouldMigrate(ClusterInvoker serviceDiscoveryInvoker, ClusterInvoker invoker); +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java new file mode 100644 index 00000000000..b6ec32d83dd --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java @@ -0,0 +1,396 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.client.migration; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.RegistryConstants; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.registry.Registry; +import org.apache.dubbo.registry.integration.DynamicDirectory; +import org.apache.dubbo.registry.integration.RegistryProtocol; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.cluster.Cluster; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; +import org.apache.dubbo.rpc.cluster.Directory; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationClusterInvoker; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationRule; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY; + +public class MigrationInvoker implements MigrationClusterInvoker { + private Logger logger = LoggerFactory.getLogger(MigrationInvoker.class); + + private URL url; + private URL consumerUrl; + private Cluster cluster; + private Registry registry; + private Class type; + private RegistryProtocol registryProtocol; + + private volatile ClusterInvoker invoker; + private volatile ClusterInvoker serviceDiscoveryInvoker; + private volatile ClusterInvoker currentAvailableInvoker; + + private MigrationRule rule; + + private boolean migrationMultiRegistry; + + public MigrationInvoker(RegistryProtocol registryProtocol, + Cluster cluster, + Registry registry, + Class type, + URL url, + URL consumerUrl) { + this(null, null, registryProtocol, cluster, registry, type, url, consumerUrl); + } + + public MigrationInvoker(ClusterInvoker invoker, + ClusterInvoker serviceDiscoveryInvoker, + RegistryProtocol registryProtocol, + Cluster cluster, + Registry registry, + Class type, + URL url, + URL consumerUrl) { + this.invoker = invoker; + this.serviceDiscoveryInvoker = serviceDiscoveryInvoker; + this.registryProtocol = registryProtocol; + this.cluster = cluster; + this.registry = registry; + this.type = type; + this.url = url; + this.consumerUrl = consumerUrl; + this.migrationMultiRegistry = url.getParameter(RegistryConstants.MIGRATION_MULTI_REGISTRY, false); + } + + public ClusterInvoker getInvoker() { + return invoker; + } + + public void setInvoker(ClusterInvoker invoker) { + this.invoker = invoker; + } + + public ClusterInvoker getServiceDiscoveryInvoker() { + return serviceDiscoveryInvoker; + } + + public void setServiceDiscoveryInvoker(ClusterInvoker serviceDiscoveryInvoker) { + this.serviceDiscoveryInvoker = serviceDiscoveryInvoker; + } + + @Override + public Class getInterface() { + return type; + } + + @Override + public synchronized void migrateToServiceDiscoveryInvoker(boolean forceMigrate) { + if (!forceMigrate) { + refreshServiceDiscoveryInvoker(); + refreshInterfaceInvoker(); + setListener(invoker, () -> { + this.compareAddresses(serviceDiscoveryInvoker, invoker); + }); + setListener(serviceDiscoveryInvoker, () -> { + this.compareAddresses(serviceDiscoveryInvoker, invoker); + }); + } else { + refreshServiceDiscoveryInvoker(); + setListener(serviceDiscoveryInvoker, () -> { + this.destroyInterfaceInvoker(this.invoker); + }); + } + } + + @Override + public void reRefer(URL newSubscribeUrl) { + // update url to prepare for migration refresh + this.url = url.addParameter(REFER_KEY, StringUtils.toQueryString(newSubscribeUrl.getParameters())); + + // re-subscribe immediately + if (invoker != null && !invoker.isDestroyed()) { + doReSubscribe(invoker, newSubscribeUrl); + } + if (serviceDiscoveryInvoker != null && !serviceDiscoveryInvoker.isDestroyed()) { + doReSubscribe(serviceDiscoveryInvoker, newSubscribeUrl); + } + } + + private void doReSubscribe(ClusterInvoker invoker, URL newSubscribeUrl) { + DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); + URL oldSubscribeUrl = directory.getRegisteredConsumerUrl(); + Registry registry = directory.getRegistry(); + registry.unregister(directory.getRegisteredConsumerUrl()); + directory.unSubscribe(RegistryProtocol.toSubscribeUrl(oldSubscribeUrl)); + registry.register(directory.getRegisteredConsumerUrl()); + + directory.setRegisteredConsumerUrl(newSubscribeUrl); + directory.buildRouterChain(newSubscribeUrl); + directory.subscribe(RegistryProtocol.toSubscribeUrl(newSubscribeUrl)); + } + + @Override + public synchronized void fallbackToInterfaceInvoker() { + refreshInterfaceInvoker(); + setListener(invoker, () -> { + this.destroyServiceDiscoveryInvoker(this.serviceDiscoveryInvoker); + }); + } + + @Override + public Result invoke(Invocation invocation) throws RpcException { + if (!checkInvokerAvailable(serviceDiscoveryInvoker)) { + if (logger.isDebugEnabled()) { + logger.debug("Using interface addresses to handle invocation, interface " + type.getName() + ", total address size " + (invoker.getDirectory().getAllInvokers() == null ? "is null" : invoker.getDirectory().getAllInvokers().size())); + } + return invoker.invoke(invocation); + } + + if (!checkInvokerAvailable(invoker)) { + if (logger.isDebugEnabled()) { + logger.debug("Using instance addresses to handle invocation, interface " + type.getName() + ", total address size " + (serviceDiscoveryInvoker.getDirectory().getAllInvokers() == null ? " is null " : serviceDiscoveryInvoker.getDirectory().getAllInvokers().size())); + } + return serviceDiscoveryInvoker.invoke(invocation); + } + + return currentAvailableInvoker.invoke(invocation); + } + + @Override + public boolean isAvailable() { + return (invoker != null && invoker.isAvailable()) + || (serviceDiscoveryInvoker != null && serviceDiscoveryInvoker.isAvailable()); + } + + @Override + public void destroy() { + if (invoker != null) { + invoker.destroy(); + } + if (serviceDiscoveryInvoker != null) { + serviceDiscoveryInvoker.destroy(); + } + } + + @Override + public URL getUrl() { + if (invoker != null) { + return invoker.getUrl(); + } else if (serviceDiscoveryInvoker != null) { + return serviceDiscoveryInvoker.getUrl(); + } + + return consumerUrl; + } + + @Override + public URL getRegistryUrl() { + if (invoker != null) { + return invoker.getRegistryUrl(); + } else if (serviceDiscoveryInvoker != null) { + serviceDiscoveryInvoker.getRegistryUrl(); + } + return url; + } + + @Override + public Directory getDirectory() { + if (invoker != null) { + return invoker.getDirectory(); + } else if (serviceDiscoveryInvoker != null) { + return serviceDiscoveryInvoker.getDirectory(); + } + return null; + } + + @Override + public boolean isDestroyed() { + return (invoker == null || invoker.isDestroyed()) + && (serviceDiscoveryInvoker == null || serviceDiscoveryInvoker.isDestroyed()); + } + + + @Override + public AtomicBoolean invokersChanged() { + return invokersChanged; + } + + private volatile AtomicBoolean invokersChanged = new AtomicBoolean(true); + + private synchronized void compareAddresses(ClusterInvoker serviceDiscoveryInvoker, ClusterInvoker invoker) { + this.invokersChanged.set(true); + if (logger.isDebugEnabled()) { + logger.info(invoker.getDirectory().getAllInvokers() == null ? "null" : invoker.getDirectory().getAllInvokers().size() + ""); + } + + Set detectors = ExtensionLoader.getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances(); + if (detectors != null && detectors.stream().allMatch(migrationDetector -> migrationDetector.shouldMigrate(serviceDiscoveryInvoker, invoker))) { + discardInterfaceInvokerAddress(invoker); + } else { + discardServiceDiscoveryInvokerAddress(serviceDiscoveryInvoker); + } + } + + private synchronized void setAddressChanged() { + this.invokersChanged.set(true); + } + + @Override + public synchronized void destroyServiceDiscoveryInvoker(ClusterInvoker serviceDiscoveryInvoker) { + if (checkInvokerAvailable(this.invoker)) { + this.currentAvailableInvoker = this.invoker; + } + if (serviceDiscoveryInvoker != null) { + if (logger.isDebugEnabled()) { + logger.debug("Destroying instance address invokers, will not listen for address changes until re-subscribed, " + type.getName()); + } + serviceDiscoveryInvoker.destroy(); + } + } + + @Override + public synchronized void discardServiceDiscoveryInvokerAddress(ClusterInvoker serviceDiscoveryInvoker) { + if (checkInvokerAvailable(this.invoker)) { + this.currentAvailableInvoker = this.invoker; + } + if (serviceDiscoveryInvoker != null) { + if (logger.isDebugEnabled()) { + logger.debug("Discarding instance addresses, total size " + (null == serviceDiscoveryInvoker.getDirectory().getAllInvokers() ? "null" : serviceDiscoveryInvoker.getDirectory().getAllInvokers().size())); + } + serviceDiscoveryInvoker.getDirectory().discordAddresses(); + } + } + + @Override + public synchronized void refreshServiceDiscoveryInvoker() { + clearListener(serviceDiscoveryInvoker); + if (needRefresh(serviceDiscoveryInvoker)) { + if (logger.isDebugEnabled()) { + logger.debug("Re-subscribing instance addresses, current interface " + type.getName()); + } + serviceDiscoveryInvoker = registryProtocol.getServiceDiscoveryInvoker(cluster, registry, type, url); + + if (migrationMultiRegistry) { + setListener(serviceDiscoveryInvoker, () -> { + this.setAddressChanged(); + }); + } + } + } + + private void clearListener(ClusterInvoker invoker) { + if (migrationMultiRegistry) { + return; + } + + if (invoker == null) { + return; + } + DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); + directory.setInvokersChangedListener(null); + } + + private void setListener(ClusterInvoker invoker, InvokersChangedListener listener) { + if (invoker == null) { + return; + } + DynamicDirectory directory = (DynamicDirectory) invoker.getDirectory(); + directory.setInvokersChangedListener(listener); + } + + @Override + public synchronized void refreshInterfaceInvoker() { + clearListener(invoker); + if (needRefresh(invoker)) { + // FIXME invoker.destroy(); + if (logger.isDebugEnabled()) { + logger.debug("Re-subscribing interface addresses for interface " + type.getName()); + } + invoker = registryProtocol.getInvoker(cluster, registry, type, url); + + if (migrationMultiRegistry) { + setListener(serviceDiscoveryInvoker, () -> { + this.setAddressChanged(); + }); + } + } + } + + @Override + public synchronized void destroyInterfaceInvoker(ClusterInvoker invoker) { + if (checkInvokerAvailable(this.serviceDiscoveryInvoker)) { + this.currentAvailableInvoker = this.serviceDiscoveryInvoker; + } + if (invoker != null) { + if (logger.isDebugEnabled()) { + logger.debug("Destroying interface address invokers, will not listen for address changes until re-subscribed, " + type.getName()); + } + invoker.destroy(); + } + } + + @Override + public synchronized void discardInterfaceInvokerAddress(ClusterInvoker invoker) { + if (this.serviceDiscoveryInvoker != null) { + this.currentAvailableInvoker = this.serviceDiscoveryInvoker; + } + if (invoker != null) { + if (logger.isDebugEnabled()) { + logger.debug("Discarding interface addresses, total address size " + (null == invoker.getDirectory().getAllInvokers() ? "null" : invoker.getDirectory().getAllInvokers().size())); + } + invoker.getDirectory().discordAddresses(); + } + } + + private boolean needRefresh(ClusterInvoker invoker) { + return invoker == null || invoker.isDestroyed(); + } + + public boolean checkInvokerAvailable(ClusterInvoker invoker) { + return invoker != null && !invoker.isDestroyed() && invoker.isAvailable(); + } + + @Override + public boolean isServiceInvoker() { + return false; + } + + @Override + public MigrationRule getMigrationRule() { + return rule; + } + + @Override + public void setMigrationRule(MigrationRule rule) { + this.rule = rule; + } + + @Override + public boolean isMigrationMultiRegistry() { + return migrationMultiRegistry; + } + +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java new file mode 100644 index 00000000000..5e60657774c --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.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.dubbo.registry.client.migration; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationRule; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationStep; + +@Activate +public class MigrationRuleHandler { + private static final Logger logger = LoggerFactory.getLogger(MigrationRuleHandler.class); + + private MigrationInvoker migrationInvoker; + + public MigrationRuleHandler(MigrationInvoker invoker) { + this.migrationInvoker = invoker; + } + + private MigrationStep currentStep; + + public void doMigrate(String rawRule) { + MigrationRule rule = MigrationRule.parse(rawRule); + + if (null != currentStep && currentStep.equals(rule.getStep())) { + if (logger.isInfoEnabled()) { + logger.info("Migration step is not change. rule.getStep is " + currentStep.name()); + } + return; + } else { + currentStep = rule.getStep(); + } + + migrationInvoker.setMigrationRule(rule); + + if (migrationInvoker.isMigrationMultiRegistry()) { + if (migrationInvoker.isServiceInvoker()) { + migrationInvoker.refreshServiceDiscoveryInvoker(); + } else { + migrationInvoker.refreshInterfaceInvoker(); + } + } else { + switch (rule.getStep()) { + case APPLICATION_FIRST: + migrationInvoker.migrateToServiceDiscoveryInvoker(false); + break; + case FORCE_APPLICATION: + migrationInvoker.migrateToServiceDiscoveryInvoker(true); + break; + case FORCE_INTERFACE: + default: + migrationInvoker.fallbackToInterfaceInvoker(); + } + } + } +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java new file mode 100644 index 00000000000..e34d6763655 --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.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.dubbo.registry.client.migration; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent; +import org.apache.dubbo.common.config.configcenter.ConfigurationListener; +import org.apache.dubbo.common.config.configcenter.DynamicConfiguration; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.ConcurrentHashSet; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.registry.integration.RegistryProtocol; +import org.apache.dubbo.registry.integration.RegistryProtocolListener; +import org.apache.dubbo.rpc.Exporter; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationRule; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import java.util.Optional; +import java.util.Set; + +import static org.apache.dubbo.common.constants.RegistryConstants.INIT; + +@Activate +public class MigrationRuleListener implements RegistryProtocolListener, ConfigurationListener { + private static final Logger logger = LoggerFactory.getLogger(MigrationRuleListener.class); + + private Set listeners = new ConcurrentHashSet<>(); + private DynamicConfiguration configuration; + + private volatile String rawRule; + + public MigrationRuleListener() { + Optional optional = ApplicationModel.getEnvironment().getDynamicConfiguration(); + + if (optional.isPresent()) { + this.configuration = optional.get(); + + logger.info("Listening for migration rules on dataId-" + MigrationRule.RULE_KEY + " group-" + MigrationRule.DUBBO_SERVICEDISCOVERY_MIGRATION_GROUP); + configuration.addListener(MigrationRule.RULE_KEY, MigrationRule.DUBBO_SERVICEDISCOVERY_MIGRATION_GROUP, this); + + rawRule = configuration.getConfig(MigrationRule.RULE_KEY, MigrationRule.DUBBO_SERVICEDISCOVERY_MIGRATION_GROUP); + if (StringUtils.isEmpty(rawRule)) { + rawRule = INIT; + } + + } else { + if (logger.isWarnEnabled()) { + logger.warn("config center is not configured!"); + } + + rawRule = INIT; + } + + process(new ConfigChangedEvent(MigrationRule.RULE_KEY, MigrationRule.DUBBO_SERVICEDISCOVERY_MIGRATION_GROUP, rawRule)); + } + + @Override + public synchronized void process(ConfigChangedEvent event) { + rawRule = event.getContent(); + if (StringUtils.isEmpty(rawRule)) { + logger.warn("Received empty migration rule, will ignore."); + return; + } + + logger.info("Using the following migration rule to migrate:"); + logger.info(rawRule); + + if (CollectionUtils.isNotEmpty(listeners)) { + listeners.forEach(listener -> listener.doMigrate(rawRule)); + } + } + + @Override + public synchronized void onExport(RegistryProtocol registryProtocol, Exporter exporter) { + + } + + @Override + public synchronized void onRefer(RegistryProtocol registryProtocol, ClusterInvoker invoker, URL url) { + MigrationInvoker migrationInvoker = (MigrationInvoker) invoker; + + MigrationRuleHandler migrationListener = new MigrationRuleHandler<>(migrationInvoker); + listeners.add(migrationListener); + + migrationListener.doMigrate(rawRule); + } + + @Override + public void onDestroy() { + if (null != configuration) { + configuration.removeListener(MigrationRule.RULE_KEY, this); + } + } +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java new file mode 100644 index 00000000000..feeb5b8aeb4 --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.client.migration; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.registry.Registry; +import org.apache.dubbo.registry.integration.RegistryProtocol; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.cluster.Cluster; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; + +public class ServiceDiscoveryMigrationInvoker extends MigrationInvoker { + private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryMigrationInvoker.class); + + public ServiceDiscoveryMigrationInvoker(RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class type, URL url, URL consumerUrl) { + super(registryProtocol, cluster, registry, type, url, consumerUrl); + } + + @Override + public boolean isServiceInvoker() { + return true; + } + + @Override + public synchronized void fallbackToInterfaceInvoker() { + logger.error("Service discovery registry type does not support discovery of interface level addresses, " + getRegistryUrl()); + migrateToServiceDiscoveryInvoker(true); + } + + @Override + public synchronized void migrateToServiceDiscoveryInvoker(boolean forceMigrate) { + refreshServiceDiscoveryInvoker(); + } + + @Override + public Result invoke(Invocation invocation) throws RpcException { + ClusterInvoker invoker = getServiceDiscoveryInvoker(); + if (invoker == null) { + throw new IllegalStateException("There's no service discovery invoker available for service " + invocation.getServiceName()); + } + return invoker.invoke(invocation); + } +} \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java index b6c00382093..d1636babf79 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java @@ -17,6 +17,7 @@ package org.apache.dubbo.registry.integration; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.Version; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; @@ -25,6 +26,7 @@ import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; +import org.apache.dubbo.registry.client.migration.InvokersChangedListener; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Protocol; @@ -35,19 +37,21 @@ import org.apache.dubbo.rpc.cluster.RouterFactory; import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Set; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; +import static org.apache.dubbo.common.constants.CommonConstants.DUBBO; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY; +import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.registry.Constants.REGISTER_KEY; import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY; -import static org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS; +import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS; import static org.apache.dubbo.remoting.Constants.CHECK_KEY; @@ -65,6 +69,7 @@ public abstract class DynamicDirectory extends AbstractDirectory implement protected final String serviceKey; // Initialization at construction time, assertion not null protected final Class serviceType; // Initialization at construction time, assertion not null + protected final URL directoryUrl; // Initialization at construction time, assertion not null, and always assign non null value protected final boolean multiGroup; protected Protocol protocol; // Initialization at the time of injection, the assertion is not null protected Registry registry; // Initialization at the time of injection, the assertion is not null @@ -72,7 +77,7 @@ public abstract class DynamicDirectory extends AbstractDirectory implement protected boolean shouldRegister; protected boolean shouldSimplified; - protected volatile URL overrideConsumerUrl; // Initialization at construction time, assertion not null, and always assign non null value + protected volatile URL overrideDirectoryUrl; // Initialization at construction time, assertion not null, and always assign non null value protected volatile URL registeredConsumerUrl; @@ -84,17 +89,13 @@ public abstract class DynamicDirectory extends AbstractDirectory implement */ protected volatile List configurators; // The initial value is null and the midway may be assigned to null, please use the local variable reference - // Map cache service url to invoker mapping. - protected volatile Map> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference protected volatile List> invokers; - // Set cache invokeUrls to invokers mapping. - protected volatile Set cachedInvokerUrls; // The initial value is null and the midway may be assigned to null, please use the local variable reference protected ServiceInstancesChangedListener serviceListener; public DynamicDirectory(Class serviceType, URL url) { - super(url); + super(url, true); if (serviceType == null) { throw new IllegalArgumentException("service type is null."); } @@ -107,7 +108,8 @@ public DynamicDirectory(Class serviceType, URL url) { this.serviceType = serviceType; this.serviceKey = super.getConsumerUrl().getServiceKey(); - String group = queryMap.get(GROUP_KEY) != null ? queryMap.get(GROUP_KEY) : ""; + this.overrideDirectoryUrl = this.directoryUrl = turnRegistryUrlToConsumerUrl(url); + String group = directoryUrl.getParameter(GROUP_KEY, ""); this.multiGroup = group != null && (ANY_VALUE.equals(group) || group.contains(",")); } @@ -116,6 +118,18 @@ public void addServiceListener(ServiceInstancesChangedListener instanceListener) this.serviceListener = instanceListener; } + private URL turnRegistryUrlToConsumerUrl(URL url) { + return URLBuilder.from(url) + .setHost(queryMap.get(REGISTER_IP_KEY) == null ? url.getHost() : queryMap.get(REGISTER_IP_KEY)) + .setPort(0) + .setProtocol(queryMap.get(PROTOCOL_KEY) == null ? DUBBO : queryMap.get(PROTOCOL_KEY)) + .setPath(queryMap.get(INTERFACE_KEY)) + .clearParameters() + .addParameters(queryMap) + .removeParameter(MONITOR_KEY) + .build(); + } + public void setProtocol(Protocol protocol) { this.protocol = protocol; } @@ -177,6 +191,11 @@ public List> getAllInvokers() { return invokers; } + @Override + public URL getConsumerUrl() { + return this.overrideDirectoryUrl; + } + public URL getRegisteredConsumerUrl() { return registeredConsumerUrl; } @@ -191,41 +210,76 @@ public void setRegisteredConsumerUrl(URL url) { } } + public void buildRouterChain(URL url) { + this.setRouterChain(RouterChain.buildChain(url)); + } + + public List> getInvokers() { + return invokers; + } + @Override - public boolean isAvailable() { + public void destroy() { if (isDestroyed()) { - return false; + return; } - Map> localUrlInvokerMap = urlInvokerMap; - if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) { - for (Invoker invoker : new ArrayList<>(localUrlInvokerMap.values())) { - if (invoker.isAvailable()) { - return true; - } + + // unregister. + try { + if (getRegisteredConsumerUrl() != null && registry != null && registry.isAvailable()) { + registry.unregister(getRegisteredConsumerUrl()); } + } catch (Throwable t) { + logger.warn("unexpected error when unregister service " + serviceKey + "from registry" + registry.getUrl(), t); + } + // unsubscribe. + try { + if (getConsumerUrl() != null && registry != null && registry.isAvailable()) { + registry.unsubscribe(getConsumerUrl(), this); + } + } catch (Throwable t) { + logger.warn("unexpected error when unsubscribe service " + serviceKey + "from registry" + registry.getUrl(), t); + } + super.destroy(); // must be executed after unsubscribing + try { + destroyAllInvokers(); + } catch (Throwable t) { + logger.warn("Failed to destroy service " + serviceKey, t); } - return false; - } - public void buildRouterChain(URL url) { - this.setRouterChain(RouterChain.buildChain(url)); + invokersChangedListener = null; } - /** - * Haomin: added for test purpose - */ - public Map> getUrlInvokerMap() { - return urlInvokerMap; + @Override + public void discordAddresses() { + try { + destroyAllInvokers(); + } catch (Throwable t) { + logger.warn("Failed to destroy service " + serviceKey, t); + } } - public List> getInvokers() { - return invokers; + private volatile InvokersChangedListener invokersChangedListener; + private volatile boolean addressChanged; + + public void setInvokersChangedListener(InvokersChangedListener listener) { + this.invokersChangedListener = listener; + if (addressChanged) { + if (invokersChangedListener != null) { + invokersChangedListener.onChange(); + this.addressChanged = false; + } + } } - @Override - public void setConsumerUrl(URL consumerUrl) { - this.consumerUrl = consumerUrl; - this.overrideConsumerUrl = consumerUrl; + protected void invokersChanged() { + if (invokersChangedListener != null) { + invokersChangedListener.onChange(); + this.addressChanged = false; + } else { + this.addressChanged = true; + } } + protected abstract void destroyAllInvokers(); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java index 9bc0bd479e0..62f9f7ef342 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java @@ -18,31 +18,27 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.registry.Registry; -import org.apache.dubbo.registry.client.RegistryProtocol; -import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory; +import org.apache.dubbo.registry.client.migration.MigrationInvoker; +import org.apache.dubbo.registry.support.AbstractRegistryFactory; import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.Result; -import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.ClusterInvoker; -import org.apache.dubbo.rpc.cluster.Directory; -import java.util.HashMap; -import java.util.Map; - -import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_REGISTRY_DIRECTORY_AUTO_MIGRATION; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL; -import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL; import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY; -import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; /** * RegistryProtocol */ public class InterfaceCompatibleRegistryProtocol extends RegistryProtocol { + private final static Logger logger = LoggerFactory.getLogger(InterfaceCompatibleRegistryProtocol.class); + @Override protected URL getRegistryUrl(Invoker originInvoker) { URL registryUrl = originInvoker.getUrl(); @@ -62,120 +58,30 @@ protected URL getRegistryUrl(URL url) { } @Override - protected DynamicDirectory createDirectory(Class type, URL url) { - return new RegistryDirectory<>(type, url); - } - - protected Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) { - ClusterInvoker invoker = getInvoker(cluster, registry, type, url); - ClusterInvoker serviceDiscoveryInvoker = getServiceDiscoveryInvoker(cluster, type, url); - ClusterInvoker migrationInvoker = new MigrationInvoker<>(invoker, serviceDiscoveryInvoker); - - return interceptInvoker(migrationInvoker, url); + public ClusterInvoker getInvoker(Cluster cluster, Registry registry, Class type, URL url) { + DynamicDirectory directory = new RegistryDirectory<>(type, url); + return doCreateInvoker(directory, cluster, registry, type); } - protected ClusterInvoker getServiceDiscoveryInvoker(Cluster cluster, Class type, URL url) { - Registry registry = registryFactory.getRegistry(super.getRegistryUrl(url)); - ClusterInvoker serviceDiscoveryInvoker = null; - // enable auto migration from interface address pool to instance address pool - boolean autoMigration = url.getParameter(ENABLE_REGISTRY_DIRECTORY_AUTO_MIGRATION, false); - if (autoMigration) { - DynamicDirectory serviceDiscoveryDirectory = super.createDirectory(type, url); - serviceDiscoveryDirectory.setRegistry(registry); - serviceDiscoveryDirectory.setProtocol(protocol); - Map parameters = new HashMap(serviceDiscoveryDirectory.getConsumerUrl().getParameters()); - URL urlToRegistry = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters); - if (serviceDiscoveryDirectory.isShouldRegister()) { - serviceDiscoveryDirectory.setRegisteredConsumerUrl(urlToRegistry); - registry.register(serviceDiscoveryDirectory.getRegisteredConsumerUrl()); - } - serviceDiscoveryDirectory.buildRouterChain(urlToRegistry); - serviceDiscoveryDirectory.subscribe(toSubscribeUrl(urlToRegistry)); - serviceDiscoveryInvoker = (ClusterInvoker) cluster.join(serviceDiscoveryDirectory); - } - return serviceDiscoveryInvoker; - } - - private static class MigrationInvoker implements ClusterInvoker { - private ClusterInvoker invoker; - private ClusterInvoker serviceDiscoveryInvoker; - - public MigrationInvoker(ClusterInvoker invoker, ClusterInvoker serviceDiscoveryInvoker) { - this.invoker = invoker; - this.serviceDiscoveryInvoker = serviceDiscoveryInvoker; - } - - public ClusterInvoker getInvoker() { - return invoker; - } - - public void setInvoker(ClusterInvoker invoker) { - this.invoker = invoker; - } - - public ClusterInvoker getServiceDiscoveryInvoker() { - return serviceDiscoveryInvoker; - } - - public void setServiceDiscoveryInvoker(ClusterInvoker serviceDiscoveryInvoker) { - this.serviceDiscoveryInvoker = serviceDiscoveryInvoker; - } - - @Override - public Class getInterface() { - return invoker.getInterface(); - } - - @Override - public Result invoke(Invocation invocation) throws RpcException { - if (serviceDiscoveryInvoker == null) { - return invoker.invoke(invocation); - } - - if (invoker.isDestroyed()) { - return serviceDiscoveryInvoker.invoke(invocation); - } - if (serviceDiscoveryInvoker.isAvailable()) { - invoker.destroy(); // can be destroyed asynchronously - return serviceDiscoveryInvoker.invoke(invocation); - } - return invoker.invoke(invocation); - } - - @Override - public URL getUrl() { - return invoker.getUrl(); - } - - @Override - public boolean isAvailable() { - return invoker.isAvailable() || serviceDiscoveryInvoker.isAvailable(); - } - - @Override - public void destroy() { - if (invoker != null) { - invoker.destroy(); - } - if (serviceDiscoveryInvoker != null) { - serviceDiscoveryInvoker.destroy(); - } - } - - @Override - public URL getRegistryUrl() { - return invoker.getRegistryUrl(); + @Override + public ClusterInvoker getServiceDiscoveryInvoker(Cluster cluster, Registry registry, Class type, URL url) { + try { + registry = registryFactory.getRegistry(super.getRegistryUrl(url)); + } catch (IllegalStateException e) { + String protocol = url.getProtocol(); + logger.warn(protocol + " do not support service discovery, automatically switch to interface-level service discovery."); + registry = AbstractRegistryFactory.getDefaultNopRegistryIfNotSupportServiceDiscovery(); } - @Override - public Directory getDirectory() { - return invoker.getDirectory(); - } + DynamicDirectory directory = new ServiceDiscoveryRegistryDirectory<>(type, url); + return doCreateInvoker(directory, cluster, registry, type); + } - @Override - public boolean isDestroyed() { - return invoker.isDestroyed() && serviceDiscoveryInvoker.isDestroyed(); - } + @Override + protected ClusterInvoker getMigrationInvoker(RegistryProtocol registryProtocol, Cluster cluster, Registry registry, + Class type, URL url, URL consumerUrl) { +// ClusterInvoker invoker = getInvoker(cluster, registry, type, url); + return new MigrationInvoker(registryProtocol, cluster, registry, type, url, consumerUrl); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/ConfigurationURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InvokersChangedListener.java similarity index 87% rename from dubbo-common/src/main/java/org/apache/dubbo/common/ConfigurationURL.java rename to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InvokersChangedListener.java index 20422775aac..5a55a023cc6 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/ConfigurationURL.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InvokersChangedListener.java @@ -14,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.common; +package org.apache.dubbo.registry.integration; -public class ConfigurationURL extends URL { +public interface InvokersChangedListener { + void onChange(); } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java index 7e612bca8e2..6d17521b71f 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java @@ -38,13 +38,11 @@ import org.apache.dubbo.rpc.cluster.Router; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; -import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.cluster.support.ClusterUtils; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.protocol.InvokerWrapper; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -53,6 +51,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY; @@ -87,6 +86,12 @@ public class RegistryDirectory extends DynamicDirectory implements NotifyL private static final ConsumerConfigurationListener CONSUMER_CONFIGURATION_LISTENER = new ConsumerConfigurationListener(); private ReferenceConfigurationListener referenceConfigurationListener; + // Map cache service url to invoker mapping. + // The initial value is null and the midway may be assigned to null, please use the local variable reference + protected volatile Map> urlInvokerMap; + // The initial value is null and the midway may be assigned to null, please use the local variable reference + protected volatile Set cachedInvokerUrls; + public RegistryDirectory(Class serviceType, URL url) { super(serviceType, url); } @@ -94,7 +99,6 @@ public RegistryDirectory(Class serviceType, URL url) { @Override public void subscribe(URL url) { setConsumerUrl(url); -// overrideConsumerUrl(); CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this); referenceConfigurationListener = new ReferenceConfigurationListener(this, url); registry.subscribe(url, this); @@ -108,38 +112,6 @@ public void unSubscribe(URL url) { registry.unsubscribe(url, this); } - @Override - public void destroy() { - if (isDestroyed()) { - return; - } - - // unregister. - try { - if (getRegisteredConsumerUrl() != null && registry != null && registry.isAvailable()) { - registry.unregister(getRegisteredConsumerUrl()); - } - } catch (Throwable t) { - logger.warn("unexpected error when unregister service " + serviceKey + "from registry" + registry.getUrl(), t); - } - // unsubscribe. - try { - if (getConsumerUrl() != null && registry != null && registry.isAvailable()) { - registry.unsubscribe(getConsumerUrl(), this); - } - ExtensionLoader.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension() - .removeListener(ApplicationModel.getApplication(), CONSUMER_CONFIGURATION_LISTENER); - } catch (Throwable t) { - logger.warn("unexpected error when unsubscribe service " + serviceKey + "from registry" + registry.getUrl(), t); - } - super.destroy(); // must be executed after unsubscribing - try { - destroyAllInvokers(); - } catch (Throwable t) { - logger.warn("Failed to destroy service " + serviceKey, t); - } - } - @Override public synchronized void notify(List urls) { Map> categoryUrls = urls.stream() @@ -180,9 +152,10 @@ private String judgeCategory(URL url) { return ""; } - private void refreshOverrideAndInvoker(List urls) { + // RefreshOverrideAndInvoker will be executed by registryCenter and configCenter, so it should be synchronized. + private synchronized void refreshOverrideAndInvoker(List urls) { // mock zookeeper://xxx?mock=return null - overrideConsumerUrl(); + overrideDirectoryUrl(); refreshInvoker(urls); } @@ -252,6 +225,9 @@ private void refreshInvoker(List invokerUrls) { logger.warn("destroyUnusedInvokers error. ", e); } } + + // notify invokers refreshed + this.invokersChanged(); } private List> toMergeInvokerList(List> invokers) { @@ -316,7 +292,7 @@ private Optional> toRouters(List urls) { * @return invokers */ private Map> toInvokers(List urls) { - Map> newUrlInvokerMap = new HashMap<>(); + Map> newUrlInvokerMap = new ConcurrentHashMap<>(); if (urls == null || urls.isEmpty()) { return newUrlInvokerMap; } @@ -394,10 +370,13 @@ private URL mergeUrl(URL providerUrl) { providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // Do not check whether the connection is successful or not, always create Invoker! + // The combination of directoryUrl and override is at the end of notify, which can't be handled here +// this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // Merge the provider side parameters + if ((providerUrl.getPath() == null || providerUrl.getPath() .length() == 0) && DUBBO_PROTOCOL.equals(providerUrl.getProtocol())) { // Compatible version 1.0 //fix by tony.chenl DUBBO-44 - String path = getConsumerUrl().getParameter(INTERFACE_KEY); + String path = directoryUrl.getParameter(INTERFACE_KEY); if (path != null) { int i = path.indexOf('/'); if (i >= 0) { @@ -440,7 +419,8 @@ private URL overrideWithConfigurators(List configurators, URL url) /** * Close all invokers */ - private void destroyAllInvokers() { + @Override + protected void destroyAllInvokers() { Map> localUrlInvokerMap = this.urlInvokerMap; // local reference if (localUrlInvokerMap != null) { for (Invoker invoker : new ArrayList<>(localUrlInvokerMap.values())) { @@ -453,6 +433,7 @@ private void destroyAllInvokers() { localUrlInvokerMap.clear(); } invokers = null; + cachedInvokerUrls = null; } /** @@ -470,9 +451,8 @@ private void destroyUnusedInvokers(Map> oldUrlInvokerMap, Map deleted = null; if (oldUrlInvokerMap != null) { - Collection> newInvokers = newUrlInvokerMap.values(); for (Map.Entry> entry : oldUrlInvokerMap.entrySet()) { - if (!newInvokers.contains(entry.getValue())) { + if (!newUrlInvokerMap.containsKey(entry.getKey())) { if (deleted == null) { deleted = new ArrayList<>(); } @@ -484,7 +464,7 @@ private void destroyUnusedInvokers(Map> oldUrlInvokerMap, Map invoker = oldUrlInvokerMap.remove(url); + Invoker invoker = oldUrlInvokerMap.get(url); if (invoker != null) { try { invoker.destroy(); @@ -535,6 +515,11 @@ public List> getAllInvokers() { return invokers; } + @Override + public URL getConsumerUrl() { + return this.overrideDirectoryUrl; + } + public URL getRegisteredConsumerUrl() { return registeredConsumerUrl; } @@ -555,12 +540,13 @@ public boolean isAvailable() { return false; } Map> localUrlInvokerMap = urlInvokerMap; - if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) { - for (Invoker invoker : new ArrayList<>(localUrlInvokerMap.values())) { - if (invoker.isAvailable()) { - return true; - } + try { + if (CollectionUtils.isNotEmptyMap(localUrlInvokerMap) + && localUrlInvokerMap.values().stream().anyMatch(Invoker::isAvailable)) { + return true; } + } catch (Throwable throwable) { + return true; } return false; } @@ -597,25 +583,23 @@ private boolean isNotCompatibleFor26x(URL url) { return StringUtils.isEmpty(url.getParameter(COMPATIBLE_CONFIG_KEY)); } - private void overrideConsumerUrl() { + private void overrideDirectoryUrl() { // merge override parameters - this.overrideConsumerUrl = getConsumerUrl(); - if (overrideConsumerUrl != null) { - List localConfigurators = this.configurators; // local reference - doOverrideUrl(localConfigurators); - List localAppDynamicConfigurators = CONSUMER_CONFIGURATION_LISTENER.getConfigurators(); // local reference - doOverrideUrl(localAppDynamicConfigurators); - if (referenceConfigurationListener != null) { - List localDynamicConfigurators = referenceConfigurationListener.getConfigurators(); // local reference - doOverrideUrl(localDynamicConfigurators); - } + this.overrideDirectoryUrl = directoryUrl; + List localConfigurators = this.configurators; // local reference + doOverrideUrl(localConfigurators); + List localAppDynamicConfigurators = CONSUMER_CONFIGURATION_LISTENER.getConfigurators(); // local reference + doOverrideUrl(localAppDynamicConfigurators); + if (referenceConfigurationListener != null) { + List localDynamicConfigurators = referenceConfigurationListener.getConfigurators(); // local reference + doOverrideUrl(localDynamicConfigurators); } } private void doOverrideUrl(List configurators) { if (CollectionUtils.isNotEmpty(configurators)) { for (Configurator configurator : configurators) { - this.overrideConsumerUrl = configurator.configure(overrideConsumerUrl); + this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java similarity index 88% rename from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryProtocol.java rename to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 64ffe04ed6b..35ba7f6819c 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.registry.client; +package org.apache.dubbo.registry.integration; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; @@ -31,10 +31,8 @@ import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.RegistryService; -import org.apache.dubbo.registry.integration.AbstractConfiguratorListener; -import org.apache.dubbo.registry.integration.DynamicDirectory; -import org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol; -import org.apache.dubbo.registry.integration.RegistryProtocolListener; +import org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory; +import org.apache.dubbo.registry.client.migration.ServiceDiscoveryMigrationInvoker; import org.apache.dubbo.registry.retry.ReExportTask; import org.apache.dubbo.registry.support.SkipFailbackWrapperException; import org.apache.dubbo.rpc.Exporter; @@ -48,6 +46,7 @@ import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository; import org.apache.dubbo.rpc.cluster.support.MergeableCluster; +import org.apache.dubbo.rpc.cluster.support.migration.MigrationClusterInvoker; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.protocol.InvokerWrapper; @@ -68,7 +67,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX; +import static org.apache.dubbo.common.constants.CommonConstants.HIDDEN_KEY_PREFIX; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; @@ -78,6 +77,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.ON_CONNECT_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.ON_DISCONNECT_KEY; import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY; import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP; import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE; @@ -134,8 +135,8 @@ public class RegistryProtocol implements Protocol { private final Map overrideListeners = new ConcurrentHashMap<>(); private final Map serviceConfigurationListeners = new ConcurrentHashMap<>(); private final ProviderConfigurationListener providerConfigurationListener = new ProviderConfigurationListener(); - //To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed. - //providerurl <--> exporter + // To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed. + // providerurl <--> exporter private final ConcurrentMap> bounds = new ConcurrentHashMap<>(); protected Protocol protocol; protected RegistryFactory registryFactory; @@ -144,12 +145,12 @@ public class RegistryProtocol implements Protocol { private ConcurrentMap reExportFailedTasks = new ConcurrentHashMap<>(); private HashedWheelTimer retryTimer = new HashedWheelTimer(new NamedThreadFactory("DubboReexportTimer", true), DEFAULT_REGISTRY_RETRY_PERIOD, TimeUnit.MILLISECONDS, 128); - //Filter the parameters that do not need to be output in url(Starting with .) - private static String[] getFilteredKeys(URL url) { + // get the parameters which shouldn't been displayed in url string(Starting with .) + private static String[] getHiddenKeys(URL url) { Map params = url.getParameters(); if (CollectionUtils.isNotEmptyMap(params)) { return params.keySet().stream() - .filter(k -> k.startsWith(HIDE_KEY_PREFIX)) + .filter(k -> k.startsWith(HIDDEN_KEY_PREFIX)) .toArray(String[]::new); } else { return new String[0]; @@ -177,11 +178,6 @@ public Map getOverrideListeners() { return overrideListeners; } - private void register(URL registryUrl, URL registeredProviderUrl) { - Registry registry = registryFactory.getRegistry(registryUrl); - registry.register(registeredProviderUrl); - } - private void registerStatedUrl(URL registryUrl, URL registeredProviderUrl, boolean registered) { ProviderModel model = ApplicationModel.getProviderModel(registeredProviderUrl.getServiceKey()); model.addStatedUrl(new ProviderModel.RegisterStatedURL( @@ -205,7 +201,7 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener); providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); - //export invoker + // export invoker final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl); // url to registry @@ -215,7 +211,7 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio // decide if we need to delay publish boolean register = providerUrl.getParameter(REGISTER_KEY, true); if (register) { - register(registryUrl, registeredProviderUrl); + registry.register(registeredProviderUrl); } // register stated url on provider model @@ -376,9 +372,12 @@ protected URL getRegistryUrl(URL url) { * @return url to registry. */ private URL getUrlToRegistry(final URL providerUrl, final URL registryUrl) { + + URL registeredProviderUrl = removeUselessParameters(providerUrl); + //The address you see at the registry if (!registryUrl.getParameter(SIMPLIFIED_KEY, false)) { - return providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameters( + return registeredProviderUrl.removeParameters(getHiddenKeys(registeredProviderUrl)).removeParameters( MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE, QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY, INTERFACES); } else { @@ -386,7 +385,7 @@ private URL getUrlToRegistry(final URL providerUrl, final URL registryUrl) { // if path is not the same as interface name then we should keep INTERFACE_KEY, // otherwise, the registry structure of zookeeper would be '/dubbo/path/providers', // but what we expect is '/dubbo/interface/providers' - if (!providerUrl.getPath().equals(providerUrl.getParameter(INTERFACE_KEY))) { + if (!registeredProviderUrl.getPath().equals(registeredProviderUrl.getParameter(INTERFACE_KEY))) { if (StringUtils.isNotEmpty(extraKeys)) { extraKeys += ","; } @@ -394,11 +393,20 @@ private URL getUrlToRegistry(final URL providerUrl, final URL registryUrl) { } String[] paramsToRegistry = getParamsToRegistry(DEFAULT_REGISTER_PROVIDER_KEYS , COMMA_SPLIT_PATTERN.split(extraKeys)); - return URL.valueOf(providerUrl, paramsToRegistry, providerUrl.getParameter(METHODS_KEY, (String[]) null)); + return URL.valueOf(registeredProviderUrl, paramsToRegistry, registeredProviderUrl.getParameter(METHODS_KEY, (String[]) null)); } } + /** + * Remove information that does not require registration + * @param providerUrl + * @return + */ + private URL removeUselessParameters(URL providerUrl) { + return providerUrl.removeParameters(ON_CONNECT_KEY, ON_DISCONNECT_KEY); + } + private URL getSubscribedOverrideUrl(URL registeredProviderUrl) { return registeredProviderUrl.setProtocol(PROVIDER_PROTOCOL) .addParameters(CATEGORY_KEY, CONFIGURATORS_CATEGORY, CHECK_KEY, String.valueOf(false)); @@ -444,32 +452,48 @@ public Invoker refer(Class type, URL url) throws RpcException { String group = qs.get(GROUP_KEY); if (group != null && group.length() > 0) { if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) { - return doRefer(Cluster.getCluster(MergeableCluster.NAME), registry, type, url); + return doRefer(Cluster.getCluster(MergeableCluster.NAME), registry, type, url, qs); } } Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY)); - return doRefer(cluster, registry, type, url); + return doRefer(cluster, registry, type, url, qs); } - protected Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) { - return interceptInvoker(getInvoker(cluster, registry, type, url), url); + protected Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url, Map parameters) { + URL consumerUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters); + ClusterInvoker migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl); + return interceptInvoker(migrationInvoker, url, consumerUrl); } - protected Invoker interceptInvoker(ClusterInvoker invoker, URL url) { + protected ClusterInvoker getMigrationInvoker(RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class type, URL url, URL consumerUrl) { + return new ServiceDiscoveryMigrationInvoker(registryProtocol, cluster, registry, type, url, consumerUrl); + } + + protected Invoker interceptInvoker(ClusterInvoker invoker, URL url, URL consumerUrl) { List listeners = findRegistryProtocolListeners(url); if (CollectionUtils.isEmpty(listeners)) { return invoker; } for (RegistryProtocolListener listener : listeners) { - listener.onRefer(this, invoker); + listener.onRefer(this, invoker, consumerUrl); } return invoker; } - protected ClusterInvoker getInvoker(Cluster cluster, Registry registry, Class type, URL url) { - DynamicDirectory directory = createDirectory(type, url); + public ClusterInvoker getServiceDiscoveryInvoker(Cluster cluster, Registry registry, Class type, URL url) { + DynamicDirectory directory = new ServiceDiscoveryRegistryDirectory<>(type, url); + return doCreateInvoker(directory, cluster, registry, type); + } + + public ClusterInvoker getInvoker(Cluster cluster, Registry registry, Class type, URL url) { + // FIXME, this method is currently not used, create the right registry before enable. + DynamicDirectory directory = new RegistryDirectory<>(type, url); + return doCreateInvoker(directory, cluster, registry, type); + } + + protected ClusterInvoker doCreateInvoker(DynamicDirectory directory, Cluster cluster, Registry registry, Class type) { directory.setRegistry(registry); directory.setProtocol(protocol); // all attributes of REFER_KEY @@ -485,23 +509,17 @@ protected ClusterInvoker getInvoker(Cluster cluster, Registry registry, C return (ClusterInvoker) cluster.join(directory); } - protected DynamicDirectory createDirectory(Class type, URL url) { - return new ServiceDiscoveryRegistryDirectory<>(type, url); - } - - public void reRefer(DynamicDirectory directory, URL newSubscribeUrl) { - URL oldSubscribeUrl = directory.getRegisteredConsumerUrl(); - Registry registry = directory.getRegistry(); - registry.unregister(directory.getRegisteredConsumerUrl()); - directory.unSubscribe(toSubscribeUrl(oldSubscribeUrl)); - registry.register(directory.getRegisteredConsumerUrl()); + public void reRefer(ClusterInvoker invoker, URL newSubscribeUrl) { + if (!(invoker instanceof MigrationClusterInvoker)) { + logger.error("Only invoker type of MigrationClusterInvoker supports reRefer, current invoker is " + invoker.getClass()); + return; + } - directory.setRegisteredConsumerUrl(newSubscribeUrl); - directory.buildRouterChain(newSubscribeUrl); - directory.subscribe(toSubscribeUrl(newSubscribeUrl)); + MigrationClusterInvoker migrationClusterInvoker = (MigrationClusterInvoker)invoker; + migrationClusterInvoker.reRefer(newSubscribeUrl); } - protected static URL toSubscribeUrl(URL url) { + public static URL toSubscribeUrl(URL url) { return url.addParameter(CATEGORY_KEY, PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY); } @@ -529,7 +547,7 @@ public void destroy() { } } - List> exporters = new ArrayList<>(bounds.values()); + List> exporters = new ArrayList>(bounds.values()); for (Exporter exporter : exporters) { exporter.unexport(); } @@ -544,8 +562,8 @@ public List getServers() { return protocol.getServers(); } - //Merge the urls of configurators - private static URL getConfigedInvokerUrl(List configurators, URL url) { + // merge the urls of configurators + private static URL getConfiguredInvokerUrl(List configurators, URL url) { if (configurators != null && configurators.size() > 0) { for (Configurator configurator : configurators) { url = configurator.configure(url); @@ -653,9 +671,9 @@ public synchronized void doOverrideIfNecessary() { //The current, may have been merged many times URL currentUrl = exporter.getInvoker().getUrl(); //Merged with this configuration - URL newUrl = getConfigedInvokerUrl(configurators, currentUrl); - newUrl = getConfigedInvokerUrl(providerConfigurationListener.getConfigurators(), newUrl); - newUrl = getConfigedInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey()) + URL newUrl = getConfiguredInvokerUrl(configurators, currentUrl); + newUrl = getConfiguredInvokerUrl(providerConfigurationListener.getConfigurators(), newUrl); + newUrl = getConfiguredInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey()) .getConfigurators(), newUrl); if (!currentUrl.equals(newUrl)) { RegistryProtocol.this.reExport(originInvoker, newUrl); @@ -693,7 +711,7 @@ public ServiceConfigurationListener(URL providerUrl, OverrideListener notifyList } private URL overrideUrl(URL providerUrl) { - return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl); + return RegistryProtocol.getConfiguredInvokerUrl(configurators, providerUrl); } @Override @@ -716,7 +734,7 @@ public ProviderConfigurationListener() { * @return */ private URL overrideUrl(URL providerUrl) { - return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl); + return RegistryProtocol.getConfiguredInvokerUrl(configurators, providerUrl); } @Override diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java index 5bf47cae92b..c4cade526cf 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java @@ -18,9 +18,8 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.SPI; -import org.apache.dubbo.registry.client.RegistryProtocol; import org.apache.dubbo.rpc.Exporter; -import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.cluster.ClusterInvoker; /** * RegistryProtocol listener is introduced to provide a chance to user to customize or change export and refer behavior @@ -42,9 +41,10 @@ public interface RegistryProtocolListener { * * @param registryProtocol RegistryProtocol instance * @param invoker invoker + * @param url * @see RegistryProtocol#refer(Class, URL) */ - void onRefer(RegistryProtocol registryProtocol, Invoker invoker); + void onRefer(RegistryProtocol registryProtocol, ClusterInvoker invoker, URL url); /** * Notify RegistryProtocol's listeners when the protocol is destroyed diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java index efcd7e9cc31..e680793ff5b 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java @@ -331,6 +331,9 @@ public void unsubscribe(URL url, NotifyListener listener) { if (listeners != null) { listeners.remove(listener); } + + // do not forget remove notified + notified.remove(url); } protected void recover() throws Exception { diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java index 541c22aef31..4093f5015dd 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java @@ -109,13 +109,26 @@ public static void destroyAll() { } } - @Override - public Registry getRegistry(URL url) { + private Registry getDefaultNopRegistryIfDestroyed() { if (destroyed.get()) { LOGGER.warn("All registry instances have been destroyed, failed to fetch any instance. " + "Usually, this means no need to try to do unnecessary redundant resource clearance, all registries has been taken care of."); return DEFAULT_NOP_REGISTRY; } + return null; + } + + public static Registry getDefaultNopRegistryIfNotSupportServiceDiscovery() { + return DEFAULT_NOP_REGISTRY; + } + + @Override + public Registry getRegistry(URL url) { + + Registry defaultNopRegistry = getDefaultNopRegistryIfDestroyed(); + if (null != defaultNopRegistry) { + return defaultNopRegistry; + } url = URLBuilder.from(url) .setPath(RegistryService.class.getName()) @@ -126,6 +139,13 @@ public Registry getRegistry(URL url) { // Lock the registry access process to ensure a single instance of the registry LOCK.lock(); try { + // double check + // fix https://github.com/apache/dubbo/issues/7265. + defaultNopRegistry = getDefaultNopRegistryIfDestroyed(); + if (null != defaultNopRegistry) { + return defaultNopRegistry; + } + Registry registry = REGISTRIES.get(key); if (registry != null) { return registry; diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator new file mode 100644 index 00000000000..b7fa71c2298 --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator @@ -0,0 +1 @@ +default=org.apache.dubbo.registry.client.migration.DefaultMigrationAddressComparator \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener index d60633c9199..24943f74d82 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener +++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener @@ -1 +1 @@ -service-discovery=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryProtocolListener \ No newline at end of file +migration=org.apache.dubbo.registry.client.migration.MigrationRuleListener \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol index 5dda00eb41b..4c3b1483ce4 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol +++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol @@ -1,2 +1,2 @@ registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol -service-discovery-registry=org.apache.dubbo.registry.client.RegistryProtocol \ No newline at end of file +service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ZKTools.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ZKTools.java index 6e71ae6d436..86ff3a214c9 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ZKTools.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ZKTools.java @@ -58,8 +58,8 @@ public void eventReceived(CuratorFramework client, CuratorEvent event) throws Ex } }, executor); - tesConditionRule(); - + testMigrationRule(); +// tesConditionRule(); // testStartupConfig(); // testProviderConfig(); // testPathCache(); @@ -68,6 +68,22 @@ public void eventReceived(CuratorFramework client, CuratorEvent event) throws Ex // Thread.sleep(100000); } + public static void testMigrationRule() { + String serviceStr = "---\n" + + "key: demo-consumer\n" + + "step: INTERFACE_FIRST\n" + + "..."; + try { + String servicePath = "/dubbo/config/DUBBO_SERVICEDISCOVERY_MIGRATION/demo-consumer.migration"; + if (client.checkExists().forPath(servicePath) == null) { + client.create().creatingParentsIfNeeded().forPath(servicePath); + } + setData(servicePath, serviceStr); + } catch (Exception e) { + e.printStackTrace(); + } + } + public static void testStartupConfig() { String str = "dubbo.registry.address=zookeeper://127.0.0.1:2181\n" + "dubbo.registry.group=dubboregistrygroup1\n" + diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java index 20eeed30be3..81b96b379d7 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java @@ -36,14 +36,12 @@ * * @since 2.7.5 */ -public class InMemoryServiceDiscovery implements ServiceDiscovery { +public class InMemoryServiceDiscovery extends AbstractServiceDiscovery { private final EventDispatcher dispatcher = EventDispatcher.getDefaultExtension(); private Map> repository = new HashMap<>(); - private ServiceInstance serviceInstance; - private URL registryURL; @Override @@ -77,18 +75,12 @@ public URL getUrl() { return registryURL; } - @Override - public ServiceInstance getLocalInstance() { - return serviceInstance; - } - public String toString() { return "InMemoryServiceDiscovery"; } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; + public void doRegister(ServiceInstance serviceInstance) { String serviceName = serviceInstance.getServiceName(); List serviceInstances = repository.computeIfAbsent(serviceName, s -> new LinkedList<>()); if (!serviceInstances.contains(serviceInstance)) { @@ -97,7 +89,7 @@ public void register(ServiceInstance serviceInstance) throws RuntimeException { } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { + public void doUpdate(ServiceInstance serviceInstance) { unregister(serviceInstance); register(serviceInstance); } diff --git a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/AbstractConsulRegistry.java b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/AbstractConsulRegistry.java index 8a5e6d78710..f18bc450263 100644 --- a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/AbstractConsulRegistry.java +++ b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/AbstractConsulRegistry.java @@ -23,17 +23,13 @@ public class AbstractConsulRegistry { static final String SERVICE_TAG = "dubbo"; static final String URL_META_KEY = "url"; - static final String WATCH_TIMEOUT = "consul-watch-timeout"; static final String CHECK_PASS_INTERVAL = "consul-check-pass-interval"; static final String DEREGISTER_AFTER = "consul-deregister-critical-service-after"; - static final int DEFAULT_PORT = 8500; - // default watch timeout in millisecond - static final int DEFAULT_WATCH_TIMEOUT = 60 * 1000; - // default time-to-live in millisecond static final long DEFAULT_CHECK_PASS_INTERVAL = 16000L; // default deregister critical server after static final String DEFAULT_DEREGISTER_TIME = "20s"; - + static final int PERIOD_DENOMINATOR = 8; + static final int ONE_THOUSAND = 1000; } diff --git a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulRegistry.java b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulRegistry.java index 990646ae6c0..e80d1cbfb14 100644 --- a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulRegistry.java +++ b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulRegistry.java @@ -52,6 +52,10 @@ import static java.util.concurrent.Executors.newCachedThreadPool; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; +import static org.apache.dubbo.common.constants.ConsulConstants.DEFAULT_PORT; +import static org.apache.dubbo.common.constants.ConsulConstants.DEFAULT_WATCH_TIMEOUT; +import static org.apache.dubbo.common.constants.ConsulConstants.INVALID_PORT; +import static org.apache.dubbo.common.constants.ConsulConstants.WATCH_TIMEOUT; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL; @@ -59,12 +63,11 @@ import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.CHECK_PASS_INTERVAL; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_CHECK_PASS_INTERVAL; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_DEREGISTER_TIME; -import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_PORT; -import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_WATCH_TIMEOUT; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEREGISTER_AFTER; +import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.ONE_THOUSAND; +import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.PERIOD_DENOMINATOR; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.SERVICE_TAG; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.URL_META_KEY; -import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.WATCH_TIMEOUT; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; /** @@ -84,17 +87,22 @@ public class ConsulRegistry extends FailbackRegistry { */ private String token; + private static final int CONSUL_CORE_THREAD_SIZE = 1; + + private static final int DEFAULT_INDEX = -1; + private static final int DEFAULT_WAIT_TIME = -1; + public ConsulRegistry(URL url) { super(url); token = url.getParameter(TOKEN_KEY, (String) null); String host = url.getHost(); - int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT; + int port = INVALID_PORT != url.getPort() ? url.getPort() : DEFAULT_PORT; client = new ConsulClient(host, port); checkPassInterval = url.getParameter(CHECK_PASS_INTERVAL, DEFAULT_CHECK_PASS_INTERVAL); - ttlConsulCheckExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("Ttl-Consul-Check-Executor", true)); - ttlConsulCheckExecutor.scheduleAtFixedRate(this::checkPass, checkPassInterval / 8, - checkPassInterval / 8, TimeUnit.MILLISECONDS); + ttlConsulCheckExecutor = new ScheduledThreadPoolExecutor(CONSUL_CORE_THREAD_SIZE, new NamedThreadFactory("Ttl-Consul-Check-Executor", true)); + ttlConsulCheckExecutor.scheduleAtFixedRate(this::checkPass, checkPassInterval / PERIOD_DENOMINATOR, + checkPassInterval / PERIOD_DENOMINATOR, TimeUnit.MILLISECONDS); } @Override @@ -147,13 +155,13 @@ public void doSubscribe(URL url, NotifyListener listener) { Long index; List urls; if (ANY_VALUE.equals(url.getServiceInterface())) { - Response>> response = getAllServices(-1, buildWatchTimeout(url)); + Response>> response = getAllServices(DEFAULT_INDEX, buildWatchTimeout(url)); index = response.getConsulIndex(); List services = getHealthServices(response.getValue()); urls = convert(services, url); } else { String service = url.getServiceInterface(); - Response> response = getHealthServices(service, -1, buildWatchTimeout(url)); + Response> response = getHealthServices(service, DEFAULT_INDEX, buildWatchTimeout(url)); index = response.getConsulIndex(); urls = convert(response.getValue(), url); } @@ -185,7 +193,7 @@ public List lookup(URL url) { } try { String service = url.getServiceKey(); - Response> result = getHealthServices(service, -1, buildWatchTimeout(url)); + Response> result = getHealthServices(service, DEFAULT_INDEX, buildWatchTimeout(url)); if (result == null || result.getValue() == null || result.getValue().isEmpty()) { return new ArrayList<>(); } else { @@ -247,7 +255,7 @@ private Response>> getAllServices(long index, int watch private List getHealthServices(Map> services) { return services.entrySet().stream() .filter(s -> s.getValue().contains(SERVICE_TAG)) - .map(s -> getHealthServices(s.getKey(), -1, -1).getValue()) + .map(s -> getHealthServices(s.getKey(), DEFAULT_INDEX, DEFAULT_WAIT_TIME).getValue()) .flatMap(Collection::stream) .collect(Collectors.toList()); } @@ -315,13 +323,13 @@ private String buildId(URL url) { private NewService.Check buildCheck(URL url) { NewService.Check check = new NewService.Check(); - check.setTtl((checkPassInterval / 1000) + "s"); + check.setTtl((checkPassInterval / ONE_THOUSAND) + "s"); check.setDeregisterCriticalServiceAfter(url.getParameter(DEREGISTER_AFTER, DEFAULT_DEREGISTER_TIME)); return check; } private int buildWatchTimeout(URL url) { - return url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT) / 1000; + return url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT) / ONE_THOUSAND; } private class ConsulNotifier implements Runnable { diff --git a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java index 8b0f3222079..e3505b97f97 100644 --- a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java @@ -58,19 +58,22 @@ import static java.util.concurrent.Executors.newCachedThreadPool; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR; import static org.apache.dubbo.common.constants.CommonConstants.SEMICOLON_SPLIT_PATTERN; +import static org.apache.dubbo.common.constants.ConsulConstants.DEFAULT_PORT; +import static org.apache.dubbo.common.constants.ConsulConstants.DEFAULT_WATCH_TIMEOUT; +import static org.apache.dubbo.common.constants.ConsulConstants.WATCH_TIMEOUT; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.CHECK_PASS_INTERVAL; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_CHECK_PASS_INTERVAL; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_DEREGISTER_TIME; -import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_PORT; -import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEFAULT_WATCH_TIMEOUT; import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.DEREGISTER_AFTER; -import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.WATCH_TIMEOUT; +import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.ONE_THOUSAND; +import static org.apache.dubbo.registry.consul.AbstractConsulRegistry.PERIOD_DENOMINATOR; import static org.apache.dubbo.registry.consul.ConsulParameter.ACL_TOKEN; import static org.apache.dubbo.registry.consul.ConsulParameter.CONSISTENCY_MODE; import static org.apache.dubbo.registry.consul.ConsulParameter.DEFAULT_ZONE_METADATA_NAME; import static org.apache.dubbo.registry.consul.ConsulParameter.INSTANCE_GROUP; import static org.apache.dubbo.registry.consul.ConsulParameter.INSTANCE_ZONE; import static org.apache.dubbo.registry.consul.ConsulParameter.TAGS; +import static org.apache.dubbo.common.constants.ConsulConstants.INVALID_PORT; /** * 2019-07-31 @@ -88,7 +91,7 @@ public class ConsulServiceDiscovery extends AbstractServiceDiscovery implements private ExecutorService notifierExecutor = newCachedThreadPool( new NamedThreadFactory("dubbo-service-discovery-consul-notifier", true)); private Map notifiers = new ConcurrentHashMap<>(); - private Map ttlSchedulers = new ConcurrentHashMap<>(); + private TtlScheduler ttlScheduler; private long checkPassInterval; private URL url; @@ -120,9 +123,10 @@ public void onEvent(ServiceInstancesChangedEvent event) { public void initialize(URL registryURL) throws Exception { this.url = registryURL; String host = url.getHost(); - int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT; + int port = INVALID_PORT != url.getPort() ? url.getPort() : DEFAULT_PORT; checkPassInterval = url.getParameter(CHECK_PASS_INTERVAL, DEFAULT_CHECK_PASS_INTERVAL); client = new ConsulClient(host, port); + ttlScheduler = new TtlScheduler(checkPassInterval, client); this.tag = registryURL.getParameter(QUERY_TAG); this.registeringTags.addAll(getRegisteringTags(url)); this.aclToken = ACL_TOKEN.getValue(registryURL); @@ -176,57 +180,48 @@ private List getRegisteringTags(URL url) { @Override public void destroy() { - if (!notifiers.isEmpty()) { - notifiers.forEach((key, notifier) -> notifier.stop()); - notifiers.clear(); - } - + notifiers.forEach((_k, notifier) -> { + if (notifier != null) { + notifier.stop(); + } + }); + notifiers.clear(); notifierExecutor.shutdownNow(); - - if (!ttlSchedulers.isEmpty()) { - ttlSchedulers.forEach((key, ttlScheduler) -> ttlScheduler.stop()); - ttlSchedulers.clear(); - } + ttlScheduler.stop(); } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - super.register(serviceInstance); - TtlScheduler ttlScheduler = ttlSchedulers.computeIfAbsent(serviceInstance.getServiceName(), name -> new TtlScheduler(checkPassInterval, client)); + public void doRegister(ServiceInstance serviceInstance) { NewService consulService = buildService(serviceInstance); ttlScheduler.add(consulService.getId()); client.agentServiceRegister(consulService, aclToken); } @Override - public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException { - for (String serviceName : listener.getServiceNames()) { + public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) + throws NullPointerException, IllegalArgumentException { + Set serviceNames = listener.getServiceNames(); + for (String serviceName : serviceNames) { ConsulNotifier notifier = notifiers.get(serviceName); - if (notifier == null) { Response> response = getHealthServices(serviceName, -1, buildWatchTimeout()); Long consulIndex = response.getConsulIndex(); notifier = new ConsulNotifier(serviceName, consulIndex); - notifiers.put(serviceName, notifier); } notifierExecutor.execute(notifier); } } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { - super.update(serviceInstance); + public void doUpdate(ServiceInstance serviceInstance) { // TODO // client.catalogRegister(buildCatalogService(serviceInstance)); } @Override public void unregister(ServiceInstance serviceInstance) throws RuntimeException { - TtlScheduler ttlScheduler = ttlSchedulers.get(serviceInstance.getServiceName()); String id = buildId(serviceInstance); - if (ttlScheduler != null) { - ttlScheduler.remove(id); - } + ttlScheduler.remove(id); client.agentServiceDeregister(id, aclToken); } @@ -243,7 +238,6 @@ public Set getServices() { public List getInstances(String serviceName) throws NullPointerException { Response> response = getHealthServices(serviceName, -1, buildWatchTimeout()); Long consulIndex = response.getConsulIndex(); - ConsulNotifier notifier = notifiers.get(serviceName); if (notifier == null) { notifier = new ConsulNotifier(serviceName, consulIndex); @@ -376,14 +370,14 @@ private Map decodeMetadata(Map metadata) { private NewService.Check buildCheck(ServiceInstance serviceInstance) { NewService.Check check = new NewService.Check(); - check.setTtl((checkPassInterval / 1000) + "s"); + check.setTtl((checkPassInterval / ONE_THOUSAND) + "s"); String deregister = serviceInstance.getMetadata().get(DEREGISTER_AFTER); check.setDeregisterCriticalServiceAfter(deregister == null ? DEFAULT_DEREGISTER_TIME : deregister); return check; } private int buildWatchTimeout() { - return url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT) / 1000; + return url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT) / ONE_THOUSAND; } private class ConsulNotifier implements Runnable { @@ -445,8 +439,8 @@ public TtlScheduler(long checkInterval, ConsulClient client) { public void add(String instanceId) { ScheduledFuture task = this.scheduler.scheduleAtFixedRate( new ConsulHeartbeatTask(instanceId), - checkInterval / 8, - checkInterval / 8, + checkInterval / PERIOD_DENOMINATOR, + checkInterval / PERIOD_DENOMINATOR, TimeUnit.MILLISECONDS); ScheduledFuture previousTask = this.serviceHeartbeats.put(instanceId, task); if (previousTask != null) { diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscoveryFactory.java b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java~HEAD similarity index 74% rename from dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscoveryFactory.java rename to dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java~HEAD index 7bef1f5a351..bd77db81c72 100644 --- a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscoveryFactory.java +++ b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java~HEAD @@ -14,15 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.registry.multicast; +package org.apache.dubbo.registry.consul; import org.apache.dubbo.common.URL; +import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; import org.apache.dubbo.registry.client.ServiceDiscovery; -import org.apache.dubbo.registry.client.ServiceDiscoveryFactory; -public class MulticastServiceDiscoveryFactory implements ServiceDiscoveryFactory { +public class ConsulServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { + @Override - public ServiceDiscovery getServiceDiscovery(URL registryURL) { - return new MulticastServiceDiscovery(); + protected ServiceDiscovery createDiscovery(URL registryURL) { + return new ConsulServiceDiscovery(); } + } diff --git a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java~dubbo-master b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java~dubbo-master new file mode 100644 index 00000000000..bd77db81c72 --- /dev/null +++ b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java~dubbo-master @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.consul; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; +import org.apache.dubbo.registry.client.ServiceDiscovery; + +public class ConsulServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { + + @Override + protected ServiceDiscovery createDiscovery(URL registryURL) { + return new ConsulServiceDiscovery(); + } + +} diff --git a/dubbo-registry/dubbo-registry-consul/src/test/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-consul/src/test/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryTest.java index 9f10d0ab16b..2b6f5269575 100644 --- a/dubbo-registry/dubbo-registry-consul/src/test/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryTest.java +++ b/dubbo-registry/dubbo-registry-consul/src/test/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -35,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +@Disabled public class ConsulServiceDiscoveryTest { private URL url; diff --git a/dubbo-registry/dubbo-registry-default/src/main/java/org/apache/dubbo/registry/dubbo/DubboRegistry.java b/dubbo-registry/dubbo-registry-default/src/main/java/org/apache/dubbo/registry/dubbo/DubboRegistry.java index ef9238ee04e..2b6f0d888cc 100644 --- a/dubbo-registry/dubbo-registry-default/src/main/java/org/apache/dubbo/registry/dubbo/DubboRegistry.java +++ b/dubbo-registry/dubbo-registry-default/src/main/java/org/apache/dubbo/registry/dubbo/DubboRegistry.java @@ -18,8 +18,6 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.NetUtils; @@ -43,8 +41,6 @@ */ public class DubboRegistry extends FailbackRegistry { - private final static Logger logger = LoggerFactory.getLogger(DubboRegistry.class); - // Reconnecting detection cycle: 3 seconds (unit:millisecond) private static final int RECONNECT_PERIOD_DEFAULT = 3 * 1000; diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java index 9ddf3b0651b..53e429f8c23 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.LogUtil; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; @@ -58,7 +59,9 @@ import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL; import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; @@ -68,6 +71,7 @@ import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL; import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL; +import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY; import static org.apache.dubbo.rpc.Constants.MOCK_KEY; import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK; import static org.apache.dubbo.rpc.cluster.Constants.MOCK_PROTOCOL; @@ -144,13 +148,17 @@ public void test_Constructor_WithErrorParam() { @Test public void test_Constructor_CheckStatus() throws Exception { URL url = URL.valueOf("notsupported://10.20.30.40/" + service + "?a=b").addParameterAndEncoded(REFER_KEY, - "foo=bar"); + "foo=bar&" + REGISTER_IP_KEY + "=10.20.30.40&" + INTERFACE_KEY + "=" + service); RegistryDirectory reg = getRegistryDirectory(url); Field field = reg.getClass().getSuperclass().getSuperclass().getDeclaredField("queryMap"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); Map queryMap = (Map) field.get(reg); Assertions.assertEquals("bar", queryMap.get("foo")); - Assertions.assertEquals(url.setProtocol(CONSUMER_PROTOCOL).clearParameters().addParameter("foo", "bar"), reg.getConsumerUrl()); + URL expected = url.setProtocol(DUBBO_PROTOCOL).clearParameters() + .addParameter("foo", "bar") + .addParameter(REGISTER_IP_KEY, "10.20.30.40") + .addParameter(INTERFACE_KEY, service); + Assertions.assertEquals(expected, reg.getConsumerUrl()); } @Test diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryProtocolTest.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryProtocolTest.java index 596a217ee92..aedf313ef20 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryProtocolTest.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryProtocolTest.java @@ -19,10 +19,12 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.context.ConfigManager; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.RegistryService; -import org.apache.dubbo.registry.client.RegistryProtocol; +import org.apache.dubbo.registry.integration.RegistryProtocol; import org.apache.dubbo.registry.support.AbstractRegistry; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.rpc.Exporter; @@ -37,6 +39,7 @@ import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol; import org.apache.commons.lang3.ArrayUtils; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -44,7 +47,7 @@ import java.util.ArrayList; import java.util.List; -import static org.apache.dubbo.registry.client.RegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS; +import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS; import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -61,7 +64,7 @@ public class RegistryProtocolTest { } final String service = DemoService.class.getName() + ":1.0.0"; - final String serviceUrl = "dubbo://127.0.0.1:9453/" + service + "?notify=true&methods=test1,test2&side=con&side=consumer"; + final String serviceUrl = "dubbo://127.0.0.1:9453/" + service + "?notify=true&methods=test1,test2&side=con&side=consumer®ister.ip=127.0.0.1"; final URL registryUrl = URL.valueOf("registry://127.0.0.1:9090/"); final private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); @@ -72,9 +75,18 @@ public static RegistryProtocol getRegistryProtocol() { @BeforeEach public void setUp() { ApplicationModel.setApplication("RegistryProtocolTest"); + ConfigManager configManager = ApplicationModel.getConfigManager(); + ApplicationConfig applicationConfig = new ApplicationConfig("dubbo-demo-provider"); + configManager.setApplication(applicationConfig); ApplicationModel.getServiceRepository().registerService(RegistryService.class); } + @AfterEach + public void reset() { + ApplicationModel.getConfigManager().destroy(); + } + + @Test public void testDefaultPort() { RegistryProtocol registryProtocol = getRegistryProtocol(); diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryStatusCheckerTest.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryStatusCheckerTest.java index 62dcad12671..d8ae08a4abd 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryStatusCheckerTest.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryStatusCheckerTest.java @@ -64,7 +64,7 @@ public void testCheckOK() { assertEquals(Status.Level.OK, new RegistryStatusChecker().check().getLevel()); String message = new RegistryStatusChecker().check().getMessage(); - Assertions.assertTrue(message.contains(registryUrl.getAddress() + "(connected)")); - Assertions.assertTrue(message.contains(registryUrl2.getAddress() + "(connected)")); + Assertions.assertTrue(message.contains(registryUrl.getHost() + "(connected)")); + Assertions.assertTrue(message.contains(registryUrl2.getHost() + "(connected)")); } } \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/SimpleRegistryService.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/SimpleRegistryService.java index 40c37b074dd..b3928066586 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/SimpleRegistryService.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/SimpleRegistryService.java @@ -17,8 +17,6 @@ package org.apache.dubbo.registry.dubbo; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; @@ -36,7 +34,6 @@ */ public class SimpleRegistryService extends AbstractRegistryService { - private final static Logger logger = LoggerFactory.getLogger(SimpleRegistryService.class); private final ConcurrentMap> remoteRegistered = new ConcurrentHashMap>(); private final ConcurrentMap> remoteListeners = new ConcurrentHashMap>(); private List registries; @@ -143,4 +140,4 @@ public void setRegistries(List registries) { this.registries = registries; } -} \ No newline at end of file +} diff --git a/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdRegistry.java b/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdRegistry.java index a15d7d561d2..def1c7d2623 100644 --- a/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdRegistry.java +++ b/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdRegistry.java @@ -17,8 +17,6 @@ package org.apache.dubbo.registry.etcd; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; @@ -59,8 +57,6 @@ */ public class EtcdRegistry extends FailbackRegistry { - private final static Logger logger = LoggerFactory.getLogger(EtcdRegistry.class); - private final static int DEFAULT_ETCD_PORT = 2379; private final static String DEFAULT_ROOT = "dubbo"; @@ -251,9 +247,14 @@ public void doUnsubscribe(URL url, NotifyListener listener) { if (listeners != null) { ChildListener etcdListener = listeners.get(listener); if (etcdListener != null) { - // maybe url has many subscribed paths - for (String path : toUnsubscribedPath(url)) { - etcdClient.removeChildListener(path, etcdListener); + if (ANY_VALUE.equals(url.getServiceInterface())) { + String root = toRootPath(); + etcdClient.removeChildListener(root, etcdListener); + }else { + // maybe url has many subscribed paths + for (String path : toUnsubscribedPath(url)) { + etcdClient.removeChildListener(path, etcdListener); + } } } } diff --git a/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java b/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java index 4bcdb59adbe..2ef9bac2ab0 100644 --- a/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java @@ -61,7 +61,6 @@ public class EtcdServiceDiscovery extends AbstractServiceDiscovery implements Ev EtcdClient etcdClient; EventDispatcher dispatcher; - ServiceInstance serviceInstance; @Override public void onEvent(ServiceInstancesChangedEvent event) { @@ -101,10 +100,8 @@ public void destroy() { } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - super.register(serviceInstance); + public void doRegister(ServiceInstance serviceInstance) { try { - this.serviceInstance = serviceInstance; String path = toPath(serviceInstance); // etcdClient.createEphemeral(path); etcdClient.putEphemeral(path, new Gson().toJson(serviceInstance)); @@ -127,8 +124,7 @@ String toParentPath(String serviceName) { } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { - super.register(serviceInstance); + public void doUpdate(ServiceInstance serviceInstance) { try { String path = toPath(serviceInstance); etcdClient.putEphemeral(path, new Gson().toJson(serviceInstance)); diff --git a/dubbo-registry/dubbo-registry-eureka/src/main/java/org/apache/dubbo/registry/eureka/EurekaServiceDiscovery.java b/dubbo-registry/dubbo-registry-eureka/src/main/java/org/apache/dubbo/registry/eureka/EurekaServiceDiscovery.java index dea1542773b..f167691c222 100644 --- a/dubbo-registry/dubbo-registry-eureka/src/main/java/org/apache/dubbo/registry/eureka/EurekaServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-eureka/src/main/java/org/apache/dubbo/registry/eureka/EurekaServiceDiscovery.java @@ -206,7 +206,7 @@ public void destroy() throws Exception { } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { + public void doRegister(ServiceInstance serviceInstance) { initEurekaClient(serviceInstance); setInstanceStatus(InstanceInfo.InstanceStatus.UP); } @@ -218,7 +218,7 @@ private void setInstanceStatus(InstanceInfo.InstanceStatus status) { } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { + public void doUpdate(ServiceInstance serviceInstance) { setInstanceStatus(serviceInstance.isHealthy() ? InstanceInfo.InstanceStatus.UP : InstanceInfo.InstanceStatus.UNKNOWN); } diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java index 7807fecaf80..8a9ad928ee9 100644 --- a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java +++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java @@ -17,8 +17,6 @@ package org.apache.dubbo.registry.multicast; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.ExecutorUtil; @@ -69,9 +67,6 @@ */ public class MulticastRegistry extends FailbackRegistry { - // logging output - private static final Logger logger = LoggerFactory.getLogger(MulticastRegistry.class); - private static final int DEFAULT_MULTICAST_PORT = 1234; private final InetAddress multicastAddress; diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscovery.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscovery.java deleted file mode 100644 index 234fdcb2841..00000000000 --- a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscovery.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.registry.multicast; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.registry.client.ServiceDiscovery; -import org.apache.dubbo.registry.client.ServiceInstance; - -import java.util.Collections; -import java.util.Set; - -/** - * TODO: make multicast protocol support Service Discovery - */ -public class MulticastServiceDiscovery implements ServiceDiscovery { - private URL registryURL; - private ServiceInstance serviceInstance; - - @Override - public void initialize(URL registryURL) throws Exception { - this.registryURL = registryURL; - } - - @Override - public void destroy() throws Exception { - - } - - @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; - } - - @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; - } - - @Override - public void unregister(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = null; - } - - @Override - public Set getServices() { - return Collections.singleton("Unsupported Operation"); - } - - @Override - public URL getUrl() { - return registryURL; - } - - @Override - public ServiceInstance getLocalInstance() { - return serviceInstance; - } -} diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery deleted file mode 100644 index 091b549072a..00000000000 --- a/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery +++ /dev/null @@ -1 +0,0 @@ -multicast=org.apache.dubbo.registry.multicast.MulticastServiceDiscovery \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory deleted file mode 100644 index 03eb6fa67bc..00000000000 --- a/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory +++ /dev/null @@ -1 +0,0 @@ -multicast=org.apache.dubbo.registry.multicast.MulticastServiceDiscoveryFactory \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java index 8ad2c10c945..f75bc8be6ce 100644 --- a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java +++ b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java @@ -71,7 +71,7 @@ public MultipleRegistry(URL url, boolean initServiceRegistry, boolean initRefere this.applicationName = url.getParameter(CommonConstants.APPLICATION_KEY); init(); checkApplicationName(this.applicationName); - // This urls contain parameter and it donot inherit from the parameter of url in MultipleRegistry + // This urls contain parameter and it do not inherit from the parameter of url in MultipleRegistry Map registryMap = new HashMap<>(); if (initServiceRegistry) { @@ -118,7 +118,7 @@ public URL getUrl() { @Override public boolean isAvailable() { - boolean available = serviceRegistries.isEmpty() ? true : false; + boolean available = serviceRegistries.isEmpty(); for (Registry serviceRegistry : serviceRegistries.values()) { if (serviceRegistry.isAvailable()) { available = true; @@ -128,7 +128,7 @@ public boolean isAvailable() { return false; } - available = referenceRegistries.isEmpty() ? true : false; + available = referenceRegistries.isEmpty(); for (Registry referenceRegistry : referenceRegistries.values()) { if (referenceRegistry.isAvailable()) { available = true; diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryServiceDiscovery.java b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryServiceDiscovery.java new file mode 100644 index 00000000000..da6d11181e1 --- /dev/null +++ b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryServiceDiscovery.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.registry.multiple; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.utils.DefaultPage; +import org.apache.dubbo.common.utils.Page; +import org.apache.dubbo.event.ConditionalEventListener; +import org.apache.dubbo.registry.client.AbstractServiceDiscovery; +import org.apache.dubbo.registry.client.ServiceDiscovery; +import org.apache.dubbo.registry.client.ServiceDiscoveryFactory; +import org.apache.dubbo.registry.client.ServiceInstance; +import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; +import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class MultipleRegistryServiceDiscovery extends AbstractServiceDiscovery { + public static final String REGISTRY_PREFIX_KEY = "child."; + private final Map serviceDiscoveries = new ConcurrentHashMap<>(); + private URL registryURL; + private String applicationName; + + @Override + public void initialize(URL registryURL) throws Exception { + this.registryURL = registryURL; + this.applicationName = registryURL.getParameter(CommonConstants.APPLICATION_KEY); + + Map parameters = registryURL.getParameters(); + for (String key : parameters.keySet()) { + if (key.startsWith(REGISTRY_PREFIX_KEY)) { + URL url = URL.valueOf(registryURL.getParameter(key)).addParameter(CommonConstants.APPLICATION_KEY, applicationName) + .addParameter("registry-type", "service"); + ServiceDiscovery serviceDiscovery = ServiceDiscoveryFactory.getExtension(url).getServiceDiscovery(url); + serviceDiscovery.initialize(url); + serviceDiscoveries.put(key, serviceDiscovery); + } + } + } + + @Override + public URL getUrl() { + return registryURL; + } + + @Override + public void destroy() throws Exception { + for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { + serviceDiscovery.destroy(); + } + } + + @Override + public void doRegister(ServiceInstance serviceInstance) { + serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.register(serviceInstance)); + } + + @Override + public void doUpdate(ServiceInstance serviceInstance) { + serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.update(serviceInstance)); + } + + @Override + public void unregister(ServiceInstance serviceInstance) throws RuntimeException { + serviceDiscoveries.values().forEach(serviceDiscovery -> serviceDiscovery.unregister(serviceInstance)); + } + + @Override + public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException { + MultiServiceInstancesChangedListener multiListener = new MultiServiceInstancesChangedListener(listener); + + for (String registryKey : serviceDiscoveries.keySet()) { + SingleServiceInstancesChangedListener singleListener = new SingleServiceInstancesChangedListener(listener.getServiceNames(), serviceDiscoveries.get(registryKey), multiListener); + multiListener.putSingleListener(registryKey, singleListener); + serviceDiscoveries.get(registryKey).addServiceInstancesChangedListener(singleListener); + } + } + + @Override + public Page getInstances(String serviceName, int offset, int pageSize, boolean healthyOnly) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { + + List serviceInstanceList = new ArrayList<>(); + for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { + Page serviceInstancePage = serviceDiscovery.getInstances(serviceName, offset, pageSize, healthyOnly); + serviceInstanceList.addAll(serviceInstancePage.getData()); + } + + return new DefaultPage<>(offset, pageSize, serviceInstanceList, serviceInstanceList.size()); + } + + @Override + public Set getServices() { + Set services = new HashSet<>(); + for (ServiceDiscovery serviceDiscovery : serviceDiscoveries.values()) { + services.addAll(serviceDiscovery.getServices()); + } + return services; + } + + protected static class MultiServiceInstancesChangedListener implements ConditionalEventListener { + private final ServiceInstancesChangedListener sourceListener; + private final Map singleListenerMap = new ConcurrentHashMap<>(); + + public MultiServiceInstancesChangedListener(ServiceInstancesChangedListener sourceListener) { + this.sourceListener = sourceListener; + } + + @Override + public boolean accept(ServiceInstancesChangedEvent event) { + return sourceListener.getServiceNames().contains(event.getServiceName()); + } + + @Override + public void onEvent(ServiceInstancesChangedEvent event) { + List serviceInstances = new ArrayList<>(); + for (SingleServiceInstancesChangedListener singleListener : singleListenerMap.values()) { + if (null != singleListener.event && null != singleListener.event.getServiceInstances()) { + for (ServiceInstance serviceInstance : singleListener.event.getServiceInstances()) { + if (!serviceInstances.contains(serviceInstance)) { + serviceInstances.add(serviceInstance); + } + } + } + } + + sourceListener.onEvent(new ServiceInstancesChangedEvent(event.getServiceName(), serviceInstances)); + } + + public void putSingleListener(String registryKey, SingleServiceInstancesChangedListener singleListener) { + singleListenerMap.put(registryKey, singleListener); + } + } + + protected static class SingleServiceInstancesChangedListener extends ServiceInstancesChangedListener { + private final MultiServiceInstancesChangedListener multiListener; + volatile ServiceInstancesChangedEvent event; + + public SingleServiceInstancesChangedListener(Set serviceNames, ServiceDiscovery serviceDiscovery, MultiServiceInstancesChangedListener multiListener) { + super(serviceNames, serviceDiscovery); + this.multiListener = multiListener; + } + + @Override + public void onEvent(ServiceInstancesChangedEvent event) { + this.event = event; + if (multiListener != null) { + multiListener.onEvent(event); + } + } + } +} diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryServiceDiscoveryFactory.java b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryServiceDiscoveryFactory.java new file mode 100644 index 00000000000..37d7de2e4fb --- /dev/null +++ b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryServiceDiscoveryFactory.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.dubbo.registry.multiple; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory; +import org.apache.dubbo.registry.client.ServiceDiscovery; + +public class MultipleRegistryServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory { + @Override + protected ServiceDiscovery createDiscovery(URL registryURL) { + return new MultipleRegistryServiceDiscovery(); + } +} diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery new file mode 100644 index 00000000000..95e2d9873a6 --- /dev/null +++ b/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery @@ -0,0 +1 @@ +multiple=org.apache.dubbo.registry.multiple.MultipleRegistryServiceDiscovery \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory new file mode 100644 index 00000000000..710a2073046 --- /dev/null +++ b/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory @@ -0,0 +1 @@ +multiple=org.apache.dubbo.registry.multiple.MultipleRegistryServiceDiscoveryFactory \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java new file mode 100644 index 00000000000..6aa2bdf02b5 --- /dev/null +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.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.dubbo.registry.nacos; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ListView; + +import java.util.List; + +public class NacosNamingServiceWrapper { + + private static final String INNERCLASS_SYMBOL = "$"; + + private static final String INNERCLASS_COMPATIBLE_SYMBOL = "___"; + + private NamingService namingService; + + public NacosNamingServiceWrapper(NamingService namingService) { + this.namingService = namingService; + } + + + public String getServerStatus() { + return namingService.getServerStatus(); + } + + public void subscribe(String serviceName, EventListener eventListener) throws NacosException { + namingService.subscribe(handleInnerSymbol(serviceName), eventListener); + } + + public void subscribe(String serviceName, String group, EventListener eventListener) throws NacosException { + namingService.subscribe(handleInnerSymbol(serviceName), group, eventListener); + } + + public List getAllInstances(String serviceName, String group) throws NacosException { + return namingService.getAllInstances(handleInnerSymbol(serviceName), group); + } + + public void registerInstance(String serviceName, String group, Instance instance) throws NacosException { + namingService.registerInstance(handleInnerSymbol(serviceName), group, instance); + } + + public void deregisterInstance(String serviceName, String group, String ip, int port) throws NacosException { + namingService.deregisterInstance(handleInnerSymbol(serviceName), group, ip, port); + } + + + public void deregisterInstance(String serviceName, String group, Instance instance) throws NacosException { + namingService.deregisterInstance(handleInnerSymbol(serviceName), group, instance); + } + + public ListView getServicesOfServer(int pageNo, int pageSize, String parameter) throws NacosException { + return namingService.getServicesOfServer(pageNo, pageSize, parameter); + } + + public List selectInstances(String serviceName, boolean healthy) throws NacosException { + return namingService.selectInstances(handleInnerSymbol(serviceName), healthy); + } + + /** + * see https://github.com/apache/dubbo/issues/7129 + * nacos service name just support `0-9a-zA-Z-._:`, grpc interface is inner interface, need compatible. + */ + private String handleInnerSymbol(String serviceName) { + if (serviceName == null) { + return null; + } + return serviceName.replace(INNERCLASS_SYMBOL, INNERCLASS_COMPATIBLE_SYMBOL); + } +} diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java index c34eac80bf8..d93d8c3ed8f 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java @@ -17,25 +17,24 @@ package org.apache.dubbo.registry.nacos; -import com.alibaba.nacos.api.common.Constants; -import com.google.common.collect.Lists; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.nacos.util.NacosInstanceManageUtil; +import org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils; import org.apache.dubbo.registry.support.FailbackRegistry; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; +import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Arrays; @@ -119,11 +118,9 @@ public class NacosRegistry extends FailbackRegistry { */ private volatile ScheduledExecutorService scheduledExecutorService; - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final NacosNamingServiceWrapper namingService; - private final NamingService namingService; - - public NacosRegistry(URL url, NamingService namingService) { + public NacosRegistry(URL url, NacosNamingServiceWrapper namingService) { super(url); this.namingService = namingService; } @@ -150,6 +147,7 @@ public List lookup(final URL url) { @Override public void doRegister(URL url) { final String serviceName = getServiceName(url); + final Instance instance = createInstance(url); /** * namingService.registerInstance with {@link org.apache.dubbo.registry.support.AbstractRegistry#registryUrl} @@ -232,11 +230,7 @@ private void doSubscribe(final URL url, final NotifyListener listener, final Set * @return */ private boolean isServiceNamesWithCompatibleMode(final URL url) { - if (!isAdminProtocol(url) && createServiceName(url).isConcrete()) { - return true; - } else { - return false; - } + return !isAdminProtocol(url) && createServiceName(url).isConcrete(); } @Override @@ -324,7 +318,7 @@ private String getLegacySubscribedServiceName(URL url) { private void appendIfPresent(StringBuilder target, URL url, String parameterName) { String parameterValue = url.getParameter(parameterName); - if (!org.apache.commons.lang3.StringUtils.isBlank(parameterValue)) { + if (!StringUtils.isBlank(parameterValue)) { target.append(SERVICE_NAME_SEPARATOR).append(parameterValue); } } @@ -513,19 +507,19 @@ private void subscribeEventListener(String serviceName, final URL url, final Not } /** - * Notify the Healthy {@link Instance instances} to subscriber. + * Notify the Enabled {@link Instance instances} to subscriber. * * @param url {@link URL} * @param listener {@link NotifyListener} * @param instances all {@link Instance instances} */ private void notifySubscriber(URL url, NotifyListener listener, Collection instances) { - List healthyInstances = new LinkedList<>(instances); - if (healthyInstances.size() > 0) { - // Healthy Instances - filterHealthyInstances(healthyInstances); + List enabledInstances = new LinkedList<>(instances); + if (enabledInstances.size() > 0) { + // Instances + filterEnabledInstances(enabledInstances); } - List urls = toUrlWithEmpty(url, healthyInstances); + List urls = toUrlWithEmpty(url, enabledInstances); NacosRegistry.this.notify(url, listener, urls); } @@ -557,6 +551,7 @@ private Instance createInstance(URL url) { URL newURL = url.addParameter(CATEGORY_KEY, category); newURL = newURL.addParameter(PROTOCOL_KEY, url.getProtocol()); newURL = newURL.addParameter(PATH_KEY, url.getPath()); + newURL = newURL.addParameters(NacosNamingServiceUtils.getNacosPreservedParam(getUrl())); String ip = url.getHost(); int port = url.getPort(); Instance instance = new Instance(); @@ -588,7 +583,7 @@ private void execute(NamingServiceCallback callback) { } } - private void filterHealthyInstances(Collection instances) { + private void filterEnabledInstances(Collection instances) { filterData(instances, Instance::isEnabled); } @@ -623,7 +618,7 @@ interface NamingServiceCallback { * @param namingService {@link NamingService} * @throws NacosException */ - void callback(NamingService namingService) throws NacosException; + void callback(NacosNamingServiceWrapper namingService) throws NacosException; } -} \ No newline at end of file +} diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java index 19b00263a96..28e4ceaa23a 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java @@ -27,13 +27,13 @@ import org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -54,7 +54,7 @@ public class NacosServiceDiscovery extends AbstractServiceDiscovery { private String group; - private NamingService namingService; + private NacosNamingServiceWrapper namingService; private URL registryURL; @@ -71,20 +71,22 @@ public void destroy() { } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { - super.register(serviceInstance); + public void doRegister(ServiceInstance serviceInstance) { execute(namingService, service -> { Instance instance = toInstance(serviceInstance); + appendPreservedParam(instance); service.registerInstance(instance.getServiceName(), group, instance); }); } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { - super.update(serviceInstance); - // TODO: Nacos should support - unregister(serviceInstance); - register(serviceInstance); + public void doUpdate(ServiceInstance serviceInstance) { + if (this.serviceInstance == null) { + register(serviceInstance); + } else { + unregister(serviceInstance); + register(serviceInstance); + } } @Override @@ -136,11 +138,6 @@ public URL getUrl() { return registryURL; } - @Override - public ServiceInstance getLocalInstance() { - return null; - } - private void handleEvent(NamingEvent event, ServiceInstancesChangedListener listener) { String serviceName = event.getServiceName(); List serviceInstances = event.getInstances() @@ -149,4 +146,9 @@ private void handleEvent(NamingEvent event, ServiceInstancesChangedListener list .collect(Collectors.toList()); dispatchServiceInstancesChangedEvent(serviceName, serviceInstances); } + + private void appendPreservedParam(Instance instance) { + Map preservedParam = NacosNamingServiceUtils.getNacosPreservedParam(getUrl()); + instance.getMetadata().putAll(preservedParam); + } } diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java index cdfcf8a839a..3889280aeac 100644 --- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java +++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java @@ -22,14 +22,17 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.ServiceInstance; +import org.apache.dubbo.registry.nacos.NacosNamingServiceWrapper; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.utils.NamingUtils; +import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -101,7 +104,7 @@ public static String getGroup(URL connectionURL) { * @return {@link NamingService} * @since 2.7.5 */ - public static NamingService createNamingService(URL connectionURL) { + public static NacosNamingServiceWrapper createNamingService(URL connectionURL) { Properties nacosProperties = buildNacosProperties(connectionURL); NamingService namingService; try { @@ -112,7 +115,7 @@ public static NamingService createNamingService(URL connectionURL) { } throw new IllegalStateException(e); } - return namingService; + return new NacosNamingServiceWrapper(namingService); } private static Properties buildNacosProperties(URL url) { @@ -163,4 +166,24 @@ private static void putPropertyIfAbsent(URL url, Properties properties, String p properties.setProperty(propertyName, defaultValue); } } + + public static Map getNacosPreservedParam(URL registryUrl) { + Map map = new HashMap<>(); + if (registryUrl.getParameter(PreservedMetadataKeys.REGISTER_SOURCE) != null) { + map.put(PreservedMetadataKeys.REGISTER_SOURCE, registryUrl.getParameter(PreservedMetadataKeys.REGISTER_SOURCE)); + } + if (registryUrl.getParameter(PreservedMetadataKeys.HEART_BEAT_TIMEOUT) != null) { + map.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, registryUrl.getParameter(PreservedMetadataKeys.HEART_BEAT_TIMEOUT)); + } + if (registryUrl.getParameter(PreservedMetadataKeys.IP_DELETE_TIMEOUT) != null) { + map.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, registryUrl.getParameter(PreservedMetadataKeys.IP_DELETE_TIMEOUT)); + } + if (registryUrl.getParameter(PreservedMetadataKeys.HEART_BEAT_INTERVAL) != null) { + map.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL, registryUrl.getParameter(PreservedMetadataKeys.HEART_BEAT_INTERVAL)); + } + if (registryUrl.getParameter(PreservedMetadataKeys.INSTANCE_ID_GENERATOR) != null) { + map.put(PreservedMetadataKeys.INSTANCE_ID_GENERATOR, registryUrl.getParameter(PreservedMetadataKeys.INSTANCE_ID_GENERATOR)); + } + return map; + } } diff --git a/dubbo-registry/dubbo-registry-redis/src/main/java/org/apache/dubbo/registry/redis/RedisRegistry.java b/dubbo-registry/dubbo-registry-redis/src/main/java/org/apache/dubbo/registry/redis/RedisRegistry.java index dcf87528787..c569ed487b7 100644 --- a/dubbo-registry/dubbo-registry-redis/src/main/java/org/apache/dubbo/registry/redis/RedisRegistry.java +++ b/dubbo-registry/dubbo-registry-redis/src/main/java/org/apache/dubbo/registry/redis/RedisRegistry.java @@ -18,8 +18,6 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NamedThreadFactory; @@ -77,14 +75,8 @@ */ public class RedisRegistry extends FailbackRegistry { - private static final Logger logger = LoggerFactory.getLogger(RedisRegistry.class); - - private static final int DEFAULT_REDIS_PORT = 6379; - private final static String DEFAULT_ROOT = "dubbo"; - private static final String REDIS_MASTER_NAME_KEY = "master-name"; - private final ScheduledExecutorService expireExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryExpireTimer", true)); private final ScheduledFuture expireFuture; @@ -101,8 +93,6 @@ public class RedisRegistry extends FailbackRegistry { private volatile boolean admin = false; - private boolean replicate; - public RedisRegistry(URL url) { super(url); String type = url.getParameter(REDIS_CLIENT_KEY, MONO_REDIS); @@ -214,21 +204,11 @@ public void doRegister(URL url) { String key = toCategoryPath(url); String value = url.toFullString(); String expire = String.valueOf(System.currentTimeMillis() + expirePeriod); - boolean success = false; - RpcException exception = null; try { redisClient.hset(key, value, expire); redisClient.publish(key, REGISTER); } catch (Throwable t) { - exception = new RpcException("Failed to register service to redis registry. registry: " + url.getAddress() + ", service: " + url + ", cause: " + t.getMessage(), t); - } - - if (exception != null) { - if (success) { - logger.warn(exception.getMessage(), exception); - } else { - throw exception; - } + throw new RpcException("Failed to register service to redis registry. registry: " + url.getAddress() + ", service: " + url + ", cause: " + t.getMessage(), t); } } @@ -236,22 +216,11 @@ public void doRegister(URL url) { public void doUnregister(URL url) { String key = toCategoryPath(url); String value = url.toFullString(); - RpcException exception = null; - boolean success = false; try { redisClient.hdel(key, value); redisClient.publish(key, UNREGISTER); - success = true; } catch (Throwable t) { - exception = new RpcException("Failed to unregister service to redis registry. registry: " + url.getAddress() + ", service: " + url + ", cause: " + t.getMessage(), t); - } - - if (exception != null) { - if (success) { - logger.warn(exception.getMessage(), exception); - } else { - throw exception; - } + throw new RpcException("Failed to unregister service to redis registry. registry: " + url.getAddress() + ", service: " + url + ", cause: " + t.getMessage(), t); } } @@ -267,8 +236,6 @@ public void doSubscribe(final URL url, final NotifyListener listener) { notifier.start(); } } - boolean success = false; - RpcException exception = null; try { if (service.endsWith(ANY_VALUE)) { admin = true; @@ -287,16 +254,8 @@ public void doSubscribe(final URL url, final NotifyListener listener) { } else { doNotify(redisClient.scan(service + PATH_SEPARATOR + ANY_VALUE), url, Collections.singletonList(listener)); } - success = true; } catch (Throwable t) { - exception = new RpcException("Failed to subscribe service from redis registry. registry: " + url.getAddress() + ", service: " + url + ", cause: " + t.getMessage(), t); - } - if (exception != null) { - if (success) { - logger.warn(exception.getMessage(), exception); - } else { - throw exception; - } + throw new RpcException("Failed to subscribe service from redis registry. registry: " + url.getAddress() + ", service: " + url + ", cause: " + t.getMessage(), t); } } diff --git a/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistry.java b/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistry.java index 4c784b34b2f..536a84a1bb2 100644 --- a/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistry.java +++ b/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistry.java @@ -17,8 +17,6 @@ package org.apache.dubbo.registry.sofa; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.NotifyListener; @@ -62,8 +60,6 @@ */ public class SofaRegistry extends FailbackRegistry { - private static final Logger LOGGER = LoggerFactory.getLogger(SofaRegistry.class); - /** * Cache subscriber by dataId */ @@ -85,8 +81,8 @@ public class SofaRegistry extends FailbackRegistry { */ public SofaRegistry(URL url) { super(url); - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Build sofa registry by url:" + url); + if (logger.isInfoEnabled()) { + logger.info("Build sofa registry by url:" + url); } this.registryClient = buildClient(url); this.waitAddressTimeout = Integer.parseInt(ConfigUtils.getProperty(ADDRESS_WAIT_TIME_KEY, "5000")); @@ -162,7 +158,7 @@ public void doSubscribe(URL url, final NotifyListener listener) { Subscriber listSubscriber = subscribers.get(serviceName); if (listSubscriber != null) { - LOGGER.warn("Service name [" + serviceName + "] have bean registered in SOFARegistry."); + logger.warn("Service name [" + serviceName + "] have bean registered in SOFARegistry."); CountDownLatch countDownLatch = new CountDownLatch(1); handleRegistryData(listSubscriber.peekData(), listener, countDownLatch); @@ -189,10 +185,10 @@ public void doSubscribe(URL url, final NotifyListener listener) { private void waitAddress(String serviceName, CountDownLatch countDownLatch) { try { if (!countDownLatch.await(waitAddressTimeout, TimeUnit.MILLISECONDS)) { - LOGGER.warn("Subscribe data failed by dataId " + serviceName); + logger.warn("Subscribe data failed by dataId " + serviceName); } } catch (Exception e) { - LOGGER.error("Error when wait Address!", e); + logger.error("Error when wait Address!", e); } } @@ -264,8 +260,8 @@ protected void printAddressData(String dataId, UserData userData) { for (String provider : datas) { sb.append(" >>> ").append(provider).append("\n"); } - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Receive updated RPC service addresses: service[" + dataId + if (logger.isInfoEnabled()) { + logger.info("Receive updated RPC service addresses: service[" + dataId + "]\n .Available target addresses size [" + datas.size() + "]\n" + sb.toString()); } diff --git a/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java b/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java index 77e7441773d..3f9b2bb0bb7 100644 --- a/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java @@ -22,8 +22,8 @@ import org.apache.dubbo.common.utils.ConfigUtils; import org.apache.dubbo.common.utils.DefaultPage; import org.apache.dubbo.common.utils.Page; +import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.DefaultServiceInstance; -import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; @@ -53,10 +53,12 @@ import static org.apache.dubbo.registry.sofa.SofaRegistryConstants.LOCAL_DATA_CENTER; import static org.apache.dubbo.registry.sofa.SofaRegistryConstants.LOCAL_REGION; -public class SofaRegistryServiceDiscovery implements ServiceDiscovery { + +public class SofaRegistryServiceDiscovery extends AbstractServiceDiscovery { + private static final Logger LOGGER = LoggerFactory.getLogger(SofaRegistryServiceDiscovery.class); - private static final String DEFAULT_GROUP = "dubbo"; + private static final String DEFAULT_GROUP = "dubbo"; private URL registryURL; @@ -100,7 +102,7 @@ public void destroy() throws Exception { } @Override - public void register(ServiceInstance serviceInstance) throws RuntimeException { + public void doRegister(ServiceInstance serviceInstance) { SofaRegistryInstance sofaRegistryInstance = new SofaRegistryInstance(serviceInstance.getId(), serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getServiceName(), serviceInstance.getMetadata()); Publisher publisher = publishers.get(serviceInstance.getServiceName()); this.serviceInstance = serviceInstance; @@ -116,7 +118,7 @@ public void register(ServiceInstance serviceInstance) throws RuntimeException { } @Override - public void update(ServiceInstance serviceInstance) throws RuntimeException { + public void doUpdate(ServiceInstance serviceInstance) { register(serviceInstance); } @@ -165,7 +167,7 @@ private List handleRegistryData(String dataId, UserData userDat List datas = getUserData(dataId, userData); List serviceInstances = new ArrayList<>(datas.size()); - for (String serviceData : datas) { + for (String serviceData : datas) { SofaRegistryInstance sri = gson.fromJson(serviceData, SofaRegistryInstance.class); DefaultServiceInstance serviceInstance = new DefaultServiceInstance(sri.getId(), dataId, sri.getHost(), sri.getPort()); @@ -240,14 +242,9 @@ protected List flatUserData(UserData userData) { return result; } - @Override - public ServiceInstance getLocalInstance() { - return serviceInstance; - } - /** - * @TODO 后续确认下 * @return + * @TODO 后续确认下 */ @Override public Set getServices() { diff --git a/dubbo-registry/dubbo-registry-sofa/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-sofa/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory new file mode 100644 index 00000000000..d16d75c980b --- /dev/null +++ b/dubbo-registry/dubbo-registry-sofa/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory @@ -0,0 +1 @@ +sofa=org.apache.dubbo.registry.sofa.SofaRegistryServiceDiscoveryFactory \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java index 51fa4aa8a6b..6e01fd6cd60 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java @@ -19,8 +19,6 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.URLBuilder; import org.apache.dubbo.common.URLStrParser; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.UrlUtils; @@ -40,6 +38,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; @@ -57,12 +56,9 @@ /** * ZookeeperRegistry - * */ public class ZookeeperRegistry extends FailbackRegistry { - private final static Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class); - private final static String DEFAULT_ROOT = "dubbo"; private final String root; @@ -168,10 +164,14 @@ public void doSubscribe(final URL url, final NotifyListener listener) { } } } else { + CountDownLatch latch = new CountDownLatch(1); List urls = new ArrayList<>(); for (String path : toCategoriesPath(url)) { ConcurrentMap listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>()); - ChildListener zkListener = listeners.computeIfAbsent(listener, k -> (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, k, toUrlsWithEmpty(url, parentPath, currentChilds))); + ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, k, latch)); + if (zkListener instanceof RegistryChildListenerImpl) { + ((RegistryChildListenerImpl) zkListener).setLatch(latch); + } zkClient.create(path, false); List children = zkClient.addChildListener(path, zkListener); if (children != null) { @@ -179,6 +179,8 @@ public void doSubscribe(final URL url, final NotifyListener listener) { } } notify(url, listener, urls); + // tells the listener to run only after the sync notification of main thread finishes. + latch.countDown(); } } catch (Throwable e) { throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); @@ -312,4 +314,33 @@ private void fetchLatestAddresses() { } } + private class RegistryChildListenerImpl implements ChildListener { + + private URL url; + + private NotifyListener listener; + + private volatile CountDownLatch latch; + + RegistryChildListenerImpl(URL url, NotifyListener listener, CountDownLatch latch) { + this.url = url; + this.listener = listener; + this.latch = latch; + } + + void setLatch(CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void childChanged(String path, List children) { + try { + latch.await(); + } catch (InterruptedException e) { + logger.warn("Zookeeper children listener thread was interrupted unexpectedly, may cause race condition with the main thread."); + } + ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, path, children)); + } + } + } diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java index a4cdd6c8a16..101c5ec7270 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java @@ -23,7 +23,7 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.DefaultPage; import org.apache.dubbo.common.utils.Page; -import org.apache.dubbo.event.EventDispatcher; +import org.apache.dubbo.registry.client.AbstractServiceDiscovery; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; @@ -41,7 +41,6 @@ import java.util.concurrent.ConcurrentHashMap; import static org.apache.dubbo.common.function.ThrowableFunction.execute; -import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.isInstanceUpdated; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.ROOT_PATH; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.build; import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.buildCuratorFramework; @@ -51,26 +50,22 @@ * Zookeeper {@link ServiceDiscovery} implementation based on * Apache Curator X Discovery */ -public class ZookeeperServiceDiscovery implements ServiceDiscovery { +public class ZookeeperServiceDiscovery extends AbstractServiceDiscovery { private final Logger logger = LoggerFactory.getLogger(getClass()); private URL registryURL; - private EventDispatcher dispatcher; - private CuratorFramework curatorFramework; private String rootPath; private org.apache.curator.x.discovery.ServiceDiscovery serviceDiscovery; - private ServiceInstance serviceInstance; - /** * The Key is watched Zookeeper path, the value is an instance of {@link CuratorWatcher} */ - private final Map watcherCaches = new ConcurrentHashMap<>(); + private final Map watcherCaches = new ConcurrentHashMap<>(); @Override public void initialize(URL registryURL) throws Exception { @@ -91,24 +86,17 @@ public void destroy() throws Exception { } @Override - public ServiceInstance getLocalInstance() { - return serviceInstance; - } - - public void register(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; + public void doRegister(ServiceInstance serviceInstance) { doInServiceRegistry(serviceDiscovery -> { serviceDiscovery.registerService(build(serviceInstance)); }); } - public void update(ServiceInstance serviceInstance) throws RuntimeException { - this.serviceInstance = serviceInstance; - if (isInstanceUpdated(serviceInstance)) { - doInServiceRegistry(serviceDiscovery -> { - serviceDiscovery.updateService(build(serviceInstance)); - }); - } + @Override + public void doUpdate(ServiceInstance serviceInstance) { + doInServiceRegistry(serviceDiscovery -> { + serviceDiscovery.updateService(build(serviceInstance)); + }); } public void unregister(ServiceInstance serviceInstance) throws RuntimeException { @@ -135,25 +123,30 @@ public Page getInstances(String serviceName, int offset, int pa List serviceInstances = new LinkedList<>(); - List serviceIds = new LinkedList<>(curatorFramework.getChildren().forPath(p)); + int totalSize = 0; + try { + List serviceIds = new LinkedList<>(curatorFramework.getChildren().forPath(p)); - int totalSize = serviceIds.size(); + totalSize = serviceIds.size(); - Iterator iterator = serviceIds.iterator(); + Iterator iterator = serviceIds.iterator(); - for (int i = 0; i < offset; i++) { - if (iterator.hasNext()) { // remove the elements from 0 to offset - iterator.next(); - iterator.remove(); + for (int i = 0; i < offset; i++) { + if (iterator.hasNext()) { // remove the elements from 0 to offset + iterator.next(); + iterator.remove(); + } } - } - for (int i = 0; i < pageSize; i++) { - if (iterator.hasNext()) { - String serviceId = iterator.next(); - ServiceInstance serviceInstance = build(serviceDiscovery.queryForInstance(serviceName, serviceId)); - serviceInstances.add(serviceInstance); + for (int i = 0; i < pageSize; i++) { + if (iterator.hasNext()) { + String serviceId = iterator.next(); + ServiceInstance serviceInstance = build(serviceDiscovery.queryForInstance(serviceName, serviceId)); + serviceInstances.add(serviceInstance); + } } + } catch (KeeperException.NoNodeException e) { + logger.warn(p + " path not exist.", e); } return new DefaultPage<>(offset, pageSize, serviceInstances, totalSize); @@ -166,6 +159,14 @@ public void addServiceInstancesChangedListener(ServiceInstancesChangedListener l listener.getServiceNames().forEach(serviceName -> registerServiceWatcher(serviceName, listener)); } + @Override + public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException { + listener.getServiceNames().forEach(serviceName -> { + ZookeeperServiceDiscoveryChangeWatcher watcher = watcherCaches.remove(serviceName); + watcher.stopWatching(); + }); + } + private void doInServiceRegistry(ThrowableConsumer consumer) { ThrowableConsumer.execute(serviceDiscovery, s -> { consumer.accept(s); @@ -178,6 +179,18 @@ private R doInServiceDiscovery(ThrowableFunction new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, listener)); try { diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java index 5ee429aaddd..1e8f1712d4e 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java @@ -39,6 +39,8 @@ public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher { private final ZookeeperServiceDiscovery zookeeperServiceDiscovery; + private boolean keepWatching = true; + private final String serviceName; public ZookeeperServiceDiscoveryChangeWatcher(ZookeeperServiceDiscovery zookeeperServiceDiscovery, @@ -55,9 +57,19 @@ public void process(WatchedEvent event) throws Exception { Watcher.Event.EventType eventType = event.getType(); if (NodeChildrenChanged.equals(eventType) || NodeDataChanged.equals(eventType)) { - listener.onEvent(new ServiceInstancesChangedEvent(serviceName, zookeeperServiceDiscovery.getInstances(serviceName))); - zookeeperServiceDiscovery.registerServiceWatcher(serviceName, listener); - zookeeperServiceDiscovery.dispatchServiceInstancesChangedEvent(serviceName); + if (shouldKeepWatching()) { + listener.onEvent(new ServiceInstancesChangedEvent(serviceName, zookeeperServiceDiscovery.getInstances(serviceName))); + zookeeperServiceDiscovery.registerServiceWatcher(serviceName, listener); + zookeeperServiceDiscovery.dispatchServiceInstancesChangedEvent(serviceName); + } } } + + public boolean shouldKeepWatching() { + return keepWatching; + } + + public void stopWatching() { + this.keepWatching = false; + } } diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory new file mode 100644 index 00000000000..12262adad29 --- /dev/null +++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory @@ -0,0 +1 @@ +zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscoveryFactory \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java index 0a30a0ad709..f04a5b56e6e 100644 --- a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java +++ b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java @@ -85,7 +85,7 @@ public void testRegistration() { assertEquals(asList(serviceInstance), serviceInstances); Map metadata = new HashMap<>(); - metadata.put("message", "Hello,World"); + //metadata.put("message", "Hello,World"); serviceInstance.setMetadata(metadata); discovery.update(serviceInstance); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java index 2e08e8ffb55..6eea6b02eaa 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Request.java @@ -127,6 +127,16 @@ public void setHeartbeat(boolean isHeartbeat) { } } + public Request copy() { + Request copy = new Request(mId); + copy.mVersion = this.mVersion; + copy.mTwoWay = this.mTwoWay; + copy.mEvent = this.mEvent; + copy.mBroken = this.mBroken; + copy.mData = this.mData; + return copy; + } + @Override public String toString() { return "Request [id=" + mId + ", version=" + mVersion + ", twoway=" + mTwoWay + ", event=" + mEvent diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java index 1ea6c109512..877f2ca3835 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java @@ -17,6 +17,7 @@ package org.apache.dubbo.remoting.exchange.codec; import org.apache.dubbo.common.Version; +import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.io.Bytes; import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.logger.Logger; @@ -38,6 +39,7 @@ import org.apache.dubbo.remoting.transport.CodecSupport; import org.apache.dubbo.remoting.transport.ExceedPayloadLimitException; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -108,6 +110,14 @@ protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byt // get data length. int len = Bytes.bytes2int(header, 12); + + // When receiving response, how to exceed the length, then directly construct a response to the client. + // see more detail from https://github.com/apache/dubbo/issues/7021. + Object obj = finishRespWhenOverPayload(channel, len, header); + if (null != obj) { + return obj; + } + checkPayload(channel, len); int tt = len + HEADER_LENGTH; @@ -148,19 +158,22 @@ protected Object decodeBody(Channel channel, InputStream is, byte[] header) thro byte status = header[3]; res.setStatus(status); try { - ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto); if (status == Response.OK) { Object data; - if (res.isHeartbeat()) { - data = decodeHeartbeatData(channel, in); - } else if (res.isEvent()) { - data = decodeEventData(channel, in); + if (res.isEvent()) { + byte[] eventPayload = CodecSupport.getPayload(is); + if (CodecSupport.isHeartBeat(eventPayload, proto)) { + // heart beat response data is always null; + data = null; + } else { + data = decodeEventData(channel, CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto), eventPayload); + } } else { - data = decodeResponseData(channel, in, getRequestData(id)); + data = decodeResponseData(channel, CodecSupport.deserialize(channel.getUrl(), is, proto), getRequestData(id)); } res.setResult(data); } else { - res.setErrorMessage(in.readUTF()); + res.setErrorMessage(CodecSupport.deserialize(channel.getUrl(), is, proto).readUTF()); } } catch (Throwable t) { res.setStatus(Response.CLIENT_ERROR); @@ -176,14 +189,17 @@ protected Object decodeBody(Channel channel, InputStream is, byte[] header) thro req.setEvent(true); } try { - ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto); Object data; - if (req.isHeartbeat()) { - data = decodeHeartbeatData(channel, in); - } else if (req.isEvent()) { - data = decodeEventData(channel, in); + if (req.isEvent()) { + byte[] eventPayload = CodecSupport.getPayload(is); + if (CodecSupport.isHeartBeat(eventPayload, proto)) { + // heart beat response data is always null; + data = null; + } else { + data = decodeEventData(channel, CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto), eventPayload); + } } else { - data = decodeRequestData(channel, in); + data = decodeRequestData(channel, CodecSupport.deserialize(channel.getUrl(), is, proto)); } req.setData(data); } catch (Throwable t) { @@ -208,7 +224,7 @@ protected Object getRequestData(long id) { } protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException { - Serialization serialization = getSerialization(channel); + Serialization serialization = getSerialization(channel, req); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. @@ -231,16 +247,23 @@ protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) int savedWriteIndex = buffer.writerIndex(); buffer.writerIndex(savedWriteIndex + HEADER_LENGTH); ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer); - ObjectOutput out = serialization.serialize(channel.getUrl(), bos); - if (req.isEvent()) { - encodeEventData(channel, out, req.getData()); + + if (req.isHeartbeat()) { + // heartbeat request data is always null + bos.write(CodecSupport.getNullBytesOf(serialization)); } else { - encodeRequestData(channel, out, req.getData(), req.getVersion()); - } - out.flushBuffer(); - if (out instanceof Cleanable) { - ((Cleanable) out).cleanup(); + ObjectOutput out = serialization.serialize(channel.getUrl(), bos); + if (req.isEvent()) { + encodeEventData(channel, out, req.getData()); + } else { + encodeRequestData(channel, out, req.getData(), req.getVersion()); + } + out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } } + bos.flush(); bos.close(); int len = bos.writtenBytes(); @@ -256,7 +279,7 @@ protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException { int savedWriteIndex = buffer.writerIndex(); try { - Serialization serialization = getSerialization(channel); + Serialization serialization = getSerialization(channel, res); // header. byte[] header = new byte[HEADER_LENGTH]; // set magic number. @@ -274,21 +297,33 @@ protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response re buffer.writerIndex(savedWriteIndex + HEADER_LENGTH); ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer); - ObjectOutput out = serialization.serialize(channel.getUrl(), bos); + // encode response data or error message. if (status == Response.OK) { - if (res.isHeartbeat()) { - encodeEventData(channel, out, res.getResult()); - } else { - encodeResponseData(channel, out, res.getResult(), res.getVersion()); + if(res.isHeartbeat()){ + // heartbeat response data is always null + bos.write(CodecSupport.getNullBytesOf(serialization)); + }else { + ObjectOutput out = serialization.serialize(channel.getUrl(), bos); + if (res.isEvent()) { + encodeEventData(channel, out, res.getResult()); + } else { + encodeResponseData(channel, out, res.getResult(), res.getVersion()); + } + out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } } } else { + ObjectOutput out = serialization.serialize(channel.getUrl(), bos); out.writeUTF(res.getErrorMessage()); + out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } } - out.flushBuffer(); - if (out instanceof Cleanable) { - ((Cleanable) out).cleanup(); - } + bos.flush(); bos.close(); @@ -347,11 +382,6 @@ protected Object decodeData(ObjectInput in) throws IOException { return decodeRequestData(in); } - @Deprecated - protected Object decodeHeartbeatData(ObjectInput in) throws IOException { - return decodeEventData(null, in); - } - protected Object decodeRequestData(ObjectInput in) throws IOException { try { return in.readObject(); @@ -395,19 +425,21 @@ protected Object decodeData(Channel channel, ObjectInput in) throws IOException return decodeRequestData(channel, in); } - protected Object decodeEventData(Channel channel, ObjectInput in) throws IOException { + protected Object decodeEventData(Channel channel, ObjectInput in, byte[] eventBytes) throws IOException { try { + if (eventBytes != null) { + int dataLen = eventBytes.length; + int threshold = ConfigurationUtils.getSystemConfiguration().getInt("deserialization.event.size", 50); + if (dataLen > threshold) { + throw new IllegalArgumentException("Event data too long, actual size " + dataLen + ", threshold " + threshold + " rejected for security consideration."); + } + } return in.readEvent(); } catch (IOException | ClassNotFoundException e) { throw new IOException(StringUtils.toString("Decode dubbo protocol event failed.", e)); } } - @Deprecated - protected Object decodeHeartbeatData(Channel channel, ObjectInput in) throws IOException { - return decodeEventData(channel, in); - } - protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException { return decodeRequestData(in); } @@ -450,5 +482,26 @@ protected void encodeResponseData(Channel channel, ObjectOutput out, Object data encodeResponseData(out, data); } - + private Object finishRespWhenOverPayload(Channel channel, long size, byte[] header) { + int payload = getPayload(channel); + boolean overPayload = isOverPayload(payload, size); + if (overPayload) { + long reqId = Bytes.bytes2long(header, 4); + byte flag = header[2]; + if ((flag & FLAG_REQUEST) == 0) { + Response res = new Response(reqId); + if ((flag & FLAG_EVENT) != 0) { + res.setEvent(true); + } + // get status. + byte status = header[3]; + res.setStatus(Response.CLIENT_ERROR); + String errorMsg = "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel; + logger.error(errorMsg); + res.setErrorMessage(errorMsg); + return res; + } + } + return null; + } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java index e5acf6d54ea..beddc2dc58e 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/DefaultFuture.java @@ -261,7 +261,7 @@ private String getTimeoutMessage(boolean scan) { } private Request getRequestWithoutData() { - Request newRequest = request; + Request newRequest = request.copy(); newRequest.setData(null); return newRequest; } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java index a1c7b2596f8..3e3f6b7ccad 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java @@ -90,7 +90,8 @@ public void send(Object message) throws RemotingException { @Override public void send(Object message, boolean sent) throws RemotingException { if (closed) { - throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The channel " + this + " is closed!"); + throw new RemotingException(this.getLocalAddress(), null, + "Failed to send message " + message + ", cause: The channel " + this + " is closed!"); } if (message instanceof Request || message instanceof Response @@ -123,7 +124,8 @@ public CompletableFuture request(Object request, ExecutorService executo @Override public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { if (closed) { - throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); + throw new RemotingException(this.getLocalAddress(), null, + "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); } // create request. Request req = new Request(); @@ -147,6 +149,10 @@ public boolean isClosed() { @Override public void close() { + // If the channel has been closed, return directly. + if (closed) { + return; + } try { // graceful close DefaultFuture.closeChannel(channel); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java index 50a06c97f8e..219ca04802c 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/TelnetUtils.java @@ -115,7 +115,7 @@ public static String toTable(List header, List> table) { buf.append("\r\n"); //content for (List row : table) { - StringBuffer rowbuf = new StringBuffer(); + StringBuilder rowbuf = new StringBuilder(); rowbuf.append("|"); for (int j = 0; j < widths.length; j++) { String cell = row.get(j); @@ -125,7 +125,7 @@ public static String toTable(List header, List> table) { if (rowbuf.length() >= totalWidth) { buf.append(rowbuf.toString()); - rowbuf = new StringBuffer(); + rowbuf = new StringBuilder(); // for(int m = 0;m < maxcountbefore && maxcountbefore < totalWidth ; m++){ // rowbuf.append(" "); // } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java index 2d39c23b73b..6c0167d3b60 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java @@ -44,7 +44,7 @@ public class LogTelnetHandler implements TelnetHandler { public String telnet(Channel channel, String message) { long size = 0; File file = LoggerFactory.getFile(); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); if (message == null || message.trim().length() == 0) { buf.append("EXAMPLE: log error / log 100"); } else { diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java index 978edd81baf..b64e708765a 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.status.StatusChecker; import org.apache.dubbo.common.status.support.StatusUtils; import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.telnet.TelnetHandler; import org.apache.dubbo.remoting.telnet.support.Help; @@ -81,7 +82,7 @@ public String telnet(Channel channel, String message) { } String status = channel.getUrl().getParameter("status"); Map statuses = new HashMap(); - if (CollectionUtils.isNotEmptyMap(statuses)) { + if (StringUtils.isNotEmpty(status)) { String[] ss = COMMA_SPLIT_PATTERN.split(status); for (String s : ss) { StatusChecker handler = extensionLoader.getExtension(s); @@ -97,5 +98,4 @@ public String telnet(Channel channel, String message) { Status stat = StatusUtils.getSummaryStatus(statuses); return String.valueOf(stat.getLevel()); } - } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java index 8f6bf61cc8d..f263fd021bf 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java @@ -22,7 +22,6 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; -import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; @@ -38,6 +37,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CLIENT_THREADPOOL; import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY; /** * AbstractClient @@ -48,13 +48,14 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client private static final Logger logger = LoggerFactory.getLogger(AbstractClient.class); private final Lock connectLock = new ReentrantLock(); private final boolean needReconnect; + //issue-7054:Consumer's executor is sharing globally. protected volatile ExecutorService executor; private ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); public AbstractClient(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); - - needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false); + // set default needReconnect true when channel is not connected + needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, true); initExecutor(url); @@ -66,6 +67,7 @@ public AbstractClient(URL url, ChannelHandler handler) throws RemotingException "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t); } + try { // connect. connect(); @@ -89,7 +91,8 @@ public AbstractClient(URL url, ChannelHandler handler) throws RemotingException } private void initExecutor(URL url) { - url = ExecutorUtil.setThreadName(url, CLIENT_THREAD_POOL_NAME); + //issue-7054:Consumer's executor is sharing globally, thread name not require provider ip. + url = url.addParameter(THREAD_NAME_KEY, CLIENT_THREAD_POOL_NAME); url = url.addParameterIfAbsent(THREADPOOL_KEY, DEFAULT_CLIENT_THREADPOOL); executor = executorRepository.createExecutorIfAbsent(url); } @@ -179,27 +182,31 @@ public void send(Object message, boolean sent) throws RemotingException { } protected void connect() throws RemotingException { - connectLock.lock(); try { - if (isConnected()) { return; } + if (isClosed() || isClosing()) { + logger.warn("No need to connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: client status is closed or closing."); + return; + } + doConnect(); if (!isConnected()) { throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " - + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() - + ", cause: Connect wait timeout: " + getConnectTimeout() + "ms."); + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + + ", cause: Connect wait timeout: " + getConnectTimeout() + "ms."); } else { if (logger.isInfoEnabled()) { logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " - + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() - + ", channel is " + this.getChannel()); + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + + ", channel is " + this.getChannel()); } } @@ -208,8 +215,8 @@ protected void connect() throws RemotingException { } catch (Throwable e) { throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " - + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() - + ", cause: " + e.getMessage(), e); + + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + + ", cause: " + e.getMessage(), e); } finally { connectLock.unlock(); @@ -254,37 +261,43 @@ public void reconnect() throws RemotingException { @Override public void close() { - - try { - super.close(); - } catch (Throwable e) { - logger.warn(e.getMessage(), e); + if (isClosed()) { + logger.warn("No need to close connection to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: the client status is closed."); + return; } + connectLock.lock(); try { - if (executor != null) { - ExecutorUtil.shutdownNow(executor, 100); + if (isClosed()) { + logger.warn("No need to close connection to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() + ", cause: the client status is closed."); + return; } - } catch (Throwable e) { - logger.warn(e.getMessage(), e); - } - try { - disconnect(); - } catch (Throwable e) { - logger.warn(e.getMessage(), e); - } + try { + super.close(); + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + } - try { - doClose(); - } catch (Throwable e) { - logger.warn(e.getMessage(), e); + try { + disconnect(); + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + } + + try { + doClose(); + } catch (Throwable e) { + logger.warn(e.getMessage(), e); + } + + } finally { + connectLock.unlock(); } } @Override public void close(int timeout) { - ExecutorUtil.gracefulShutdown(executor, timeout); close(); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java index 9be96637109..fd18ed6f99f 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractCodec.java @@ -16,9 +16,6 @@ */ package org.apache.dubbo.remoting.transport; -import java.io.IOException; -import java.net.InetSocketAddress; - import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -27,6 +24,11 @@ import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.exchange.Request; +import org.apache.dubbo.remoting.exchange.Response; + +import java.io.IOException; +import java.net.InetSocketAddress; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; @@ -42,16 +44,37 @@ public abstract class AbstractCodec implements Codec2 { private static final String SERVER_SIDE = "server"; protected static void checkPayload(Channel channel, long size) throws IOException { + int payload = getPayload(channel); + boolean overPayload = isOverPayload(payload, size); + if (overPayload) { + ExceedPayloadLimitException e = new ExceedPayloadLimitException( + "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel); + logger.error(e); + throw e; + } + } + + protected static int getPayload(Channel channel) { int payload = Constants.DEFAULT_PAYLOAD; if (channel != null && channel.getUrl() != null) { payload = channel.getUrl().getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD); } + return payload; + } + + protected static boolean isOverPayload(int payload, long size) { if (payload > 0 && size > payload) { - ExceedPayloadLimitException e = new ExceedPayloadLimitException( - "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel); - logger.error(e); - throw e; + return true; } + return false; + } + + protected Serialization getSerialization(Channel channel, Request req) { + return CodecSupport.getSerialization(channel.getUrl()); + } + + protected Serialization getSerialization(Channel channel, Response res) { + return CodecSupport.getSerialization(channel.getUrl()); } protected Serialization getSerialization(Channel channel) { @@ -59,7 +82,7 @@ protected Serialization getSerialization(Channel channel) { } protected boolean isClientSide(Channel channel) { - String side = (String)channel.getAttribute(SIDE_KEY); + String side = (String) channel.getAttribute(SIDE_KEY); if (CLIENT_SIDE.equals(side)) { return true; } else if (SERVER_SIDE.equals(side)) { diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java index ef448dbf92b..967d8d233bf 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java @@ -172,8 +172,7 @@ public void connected(Channel ch) throws RemotingException { return; } - Collection channels = getChannels(); - if (accepts > 0 && channels.size() > accepts) { + if (accepts > 0 && getChannels().size() > accepts) { logger.error("Close channel " + ch + ", cause: The server " + ch.getLocalAddress() + " connections greater than max config " + accepts); ch.close(); return; diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java index 8c74fe564f6..d4beb502949 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java @@ -22,25 +22,32 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.model.ServiceRepository; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; -import static org.apache.dubbo.common.serialize.Constants.COMPACTED_JAVA_SERIALIZATION_ID; -import static org.apache.dubbo.common.serialize.Constants.JAVA_SERIALIZATION_ID; -import static org.apache.dubbo.common.serialize.Constants.NATIVE_JAVA_SERIALIZATION_ID; - public class CodecSupport { - private static final Logger logger = LoggerFactory.getLogger(CodecSupport.class); private static Map ID_SERIALIZATION_MAP = new HashMap(); private static Map ID_SERIALIZATIONNAME_MAP = new HashMap(); private static Map SERIALIZATIONNAME_ID_MAP = new HashMap(); + // Cache null object serialize results, for heartbeat request/response serialize use. + private static Map ID_NULLBYTES_MAP = new HashMap(); + + private static final ThreadLocal TL_BUFFER = ThreadLocal.withInitial(() -> new byte[1024]); static { Set supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions(); @@ -67,7 +74,7 @@ public static Serialization getSerializationById(Byte id) { return ID_SERIALIZATION_MAP.get(id); } - public static byte getIDByName(String name) { + public static Byte getIDByName(String name) { return SERIALIZATIONNAME_ID_MAP.get(name); } @@ -77,19 +84,98 @@ public static Serialization getSerialization(URL url) { } public static Serialization getSerialization(URL url, Byte id) throws IOException { - Serialization serialization = getSerializationById(id); - String serializationName = url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION); - // Check if "serialization id" passed from network matches the id on this side(only take effect for JDK serialization), for security purpose. - if (serialization == null - || ((id == JAVA_SERIALIZATION_ID || id == NATIVE_JAVA_SERIALIZATION_ID || id == COMPACTED_JAVA_SERIALIZATION_ID) - && !(serializationName.equals(ID_SERIALIZATIONNAME_MAP.get(id))))) { - throw new IOException("Unexpected serialization id:" + id + " received from network, please check if the peer send the right id."); + Serialization result = getSerializationById(id); + if (result == null) { + throw new IOException("Unrecognized serialize type from consumer: " + id); } - return serialization; + return result; } public static ObjectInput deserialize(URL url, InputStream is, byte proto) throws IOException { Serialization s = getSerialization(url, proto); return s.deserialize(url, is); } + + /** + * Get the null object serialize result byte[] of Serialization from the cache, + * if not, generate it first. + * + * @param s Serialization Instances + * @return serialize result of null object + */ + public static byte[] getNullBytesOf(Serialization s) { + return ID_NULLBYTES_MAP.computeIfAbsent(s.getContentTypeId(), k -> { + //Pre-generated Null object bytes + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] nullBytes = new byte[0]; + try { + ObjectOutput out = s.serialize(null, baos); + out.writeObject(null); + out.flushBuffer(); + nullBytes = baos.toByteArray(); + baos.close(); + } catch (Exception e) { + logger.warn("Serialization extension " + s.getClass().getName() + " not support serializing null object, return an empty bytes instead."); + } + return nullBytes; + }); + } + + /** + * Read all payload to byte[] + * + * @param is + * @return + * @throws IOException + */ + public static byte[] getPayload(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = getBuffer(is.available()); + int len; + while ((len = is.read(buffer)) > -1) { + baos.write(buffer, 0, len); + } + baos.flush(); + return baos.toByteArray(); + } + + private static byte[] getBuffer(int size) { + byte[] bytes = TL_BUFFER.get(); + if (size <= bytes.length) { + return bytes; + } + return new byte[size]; + } + + /** + * Check if payload is null object serialize result byte[] of serialization + * + * @param payload + * @param proto + * @return + */ + public static boolean isHeartBeat(byte[] payload, byte proto) { + return Arrays.equals(payload, getNullBytesOf(getSerializationById(proto))); + } + + public static void checkSerialization(String path, String version, Byte id) throws IOException { + ServiceRepository repository = ApplicationModel.getServiceRepository(); + ProviderModel providerModel = repository.lookupExportedServiceWithoutGroup(path + ":" + version); + if (providerModel == null) { + if (logger.isWarnEnabled()) { + logger.warn("Serialization security check is enabled but cannot work as expected because " + + "there's no matched provider model for path " + path + ", version " + version); + } + } else { + List urls = providerModel.getServiceConfig().getExportedUrls(); + if (CollectionUtils.isNotEmpty(urls)) { + URL url = urls.get(0); + String serializationName = url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION); + Byte localId = SERIALIZATIONNAME_ID_MAP.get(serializationName); + if (localId != null && !localId.equals(id)) { + throw new IOException("Unexpected serialization id:" + id + " received from network, please check if the peer send the right id."); + } + } + } + } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.java new file mode 100644 index 00000000000..4bdf890b974 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/MockTransporter.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.dubbo.remoting; + +import org.apache.dubbo.common.URL; + +import org.mockito.Mockito; + +public class MockTransporter implements Transporter { + private RemotingServer server = Mockito.mock(RemotingServer.class); + private Client client = Mockito.mock(Client.class); + + @Override + public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException { + return server; + } + + @Override + public Client connect(URL url, ChannelHandler handler) throws RemotingException { + return client; + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.java new file mode 100644 index 00000000000..8ebe13da12c --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/TransportersTest.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.dubbo.remoting; + +import org.apache.dubbo.common.URL; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TransportersTest { + private String url = "dubbo://127.0.0.1:12345?transporter=mockTransporter"; + private ChannelHandler channel = Mockito.mock(ChannelHandler.class); + + @Test + public void testBind() throws RemotingException { + Assertions.assertThrows(RuntimeException.class, () -> Transporters.bind((String) null)); + Assertions.assertThrows(RuntimeException.class, () -> Transporters.bind((URL) null)); + Assertions.assertThrows(RuntimeException.class, () -> Transporters.bind(url)); + Assertions.assertNotNull(Transporters.bind(url, channel)); + Assertions.assertNotNull(Transporters.bind(url, channel, channel)); + } + + @Test + public void testConnect() throws RemotingException { + Assertions.assertThrows(RuntimeException.class, () -> Transporters.connect((String) null)); + Assertions.assertThrows(RuntimeException.class, () -> Transporters.connect((URL) null)); + Assertions.assertNotNull(Transporters.connect(url)); + Assertions.assertNotNull(Transporters.connect(url, channel)); + Assertions.assertNotNull(Transporters.connect(url, channel, channel)); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java index cb99fe3664e..d801c649fc8 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java @@ -43,7 +43,6 @@ import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT; -import static org.junit.jupiter.api.Assertions.fail; /** * @@ -96,6 +95,22 @@ private byte[] getRequestBytes(Object obj, byte[] header) throws IOException { return request; } + private byte[] getReadonlyEventRequestBytes(Object obj, byte[] header) throws IOException { + // encode request data. + UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024); + ObjectOutput out = serialization.serialize(url, bos); + out.writeObject(obj); + + out.flushBuffer(); + bos.flush(); + bos.close(); + byte[] data = bos.toByteArray(); +// byte[] len = Bytes.int2bytes(data.length); + System.arraycopy(data, 0, header, 12, data.length); + byte[] request = join(header, data); + return request; + } + private byte[] assemblyDataProtocol(byte[] header) { Person request = new Person(); byte[] newbuf = join(header, objectToByte(request)); @@ -170,12 +185,19 @@ public void testInvalidSerializaitonId() throws Exception { public void test_Decode_Check_Payload() throws IOException { byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; byte[] request = assemblyDataProtocol(header); + try { - testDecode_assertEquals(request, TelnetCodec.DecodeResult.NEED_MORE_INPUT); - fail(); + Channel channel = getServerSideChannel(url); + ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(request); + Object obj = codec.decode(channel, buffer); + + Assertions.assertTrue(obj instanceof Response); + Assertions.assertTrue(((Response) obj).getErrorMessage().startsWith( + "Data length too large: " + Bytes.bytes2int(new byte[]{1, 1, 1, 1}))); } catch (IOException expected) { Assertions.assertTrue(expected.getMessage().startsWith("Data length too large: " + Bytes.bytes2int(new byte[]{1, 1, 1, 1}))); } + } @Test @@ -232,12 +254,14 @@ public void test_Decode_Return_Request_Event_Object() throws IOException { Person person = new Person(); byte[] request = getRequestBytes(person, header); + System.setProperty("deserialization.event.size", "100"); Request obj = (Request) decode(request); Assertions.assertEquals(person, obj.getData()); Assertions.assertTrue(obj.isTwoWay()); Assertions.assertTrue(obj.isEvent()); Assertions.assertEquals(Version.getProtocolVersion(), obj.getVersion()); System.out.println(obj); + System.clearProperty("deserialization.event.size"); } @Test @@ -271,7 +295,7 @@ public void test_Decode_Return_Request_Heartbeat_Object() throws IOException { @Test public void test_Decode_Return_Request_Object() throws IOException { //|10011111|20-stats=ok|id=0|length=0 - byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xe2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xc2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Person person = new Person(); byte[] request = getRequestBytes(person, header); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.java new file mode 100644 index 00000000000..0306d850e19 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ExchangersTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.exchange; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerDispatcher; +import org.apache.dubbo.remoting.exchange.support.Replier; +import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class ExchangersTest { + + @Test + public void testBind() throws RemotingException { + String url = "dubbo://127.0.0.1:12345?exchanger=mockExchanger"; + Exchangers.bind(url, Mockito.mock(Replier.class)); + Exchangers.bind(url, new ChannelHandlerAdapter(), Mockito.mock(Replier.class)); + Exchangers.bind(url, new ExchangeHandlerDispatcher()); + + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.bind((URL) null, new ExchangeHandlerDispatcher())); + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.bind(url, (ExchangeHandlerDispatcher) null)); + } + + @Test + public void testConnect() throws RemotingException { + String url = "dubbo://127.0.0.1:12345?exchanger=mockExchanger"; + Exchangers.connect(url); + Exchangers.connect(url, Mockito.mock(Replier.class)); + Exchangers.connect(URL.valueOf(url), Mockito.mock(Replier.class)); + Exchangers.connect(url, new ChannelHandlerAdapter(), Mockito.mock(Replier.class)); + Exchangers.connect(url, new ExchangeHandlerDispatcher()); + + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.connect((URL) null, new ExchangeHandlerDispatcher())); + Assertions.assertThrows(RuntimeException.class, + () -> Exchangers.connect(url, (ExchangeHandlerDispatcher) null)); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.java new file mode 100644 index 00000000000..9c820c08cf7 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/MockExchanger.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.dubbo.remoting.exchange; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.RemotingException; + +import org.mockito.Mockito; + +public class MockExchanger implements Exchanger{ + private ExchangeServer exchangeServer = Mockito.mock(ExchangeServer.class); + private ExchangeClient exchangeClient = Mockito.mock(ExchangeClient.class); + + @Override + public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { + return exchangeServer; + } + + @Override + public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { + return exchangeClient; + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java index 6482232b42c..67df3b93acf 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java @@ -17,6 +17,10 @@ package org.apache.dubbo.remoting.exchange.support; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.threadpool.ThreadlessExecutor; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.TimeoutException; import org.apache.dubbo.remoting.exchange.Request; @@ -28,6 +32,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; public class DefaultFutureTest { @@ -115,6 +120,43 @@ public void timeoutSend() throws Exception { System.out.println(e.getMessage()); } } + /** + * for example, it will print like this: + *before a future is create , time is : 2021-01-22 10:55:03 + * null + * after a future is timeout , time is : 2021-01-22 10:55:05 + */ + @Test + public void interruptSend() throws Exception { + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + System.out.println("before a future is create , time is : " + LocalDateTime.now().format(formatter)); + // timeout after 1 seconds. + Channel channel = new MockedChannel(); + int channelId = 10; + Request request = new Request(channelId); + ExecutorService sharedExecutor = ExtensionLoader.getExtensionLoader(ExecutorRepository.class) + .getDefaultExtension().createExecutorIfAbsent(URL.valueOf("dubbo://127.0.0.1:23456")); + ThreadlessExecutor executor = new ThreadlessExecutor(sharedExecutor); + DefaultFuture f = DefaultFuture.newFuture(channel, request, 1000, executor); + //mark the future is sent + DefaultFuture.sent(channel, request); + // get operate will throw a interrupted exception, because the thread is interrupted. + try { + new InterruptThread(Thread.currentThread()).start(); + executor.waitAndDrain(); + f.get(); + } catch (Exception e) { + Assertions.assertTrue(e instanceof InterruptedException, "catch exception is not interrupted exception!"); + System.out.println(e.getMessage()); + } + //waiting timeout check task finished + Thread.sleep(1500); + System.out.println("after a future is timeout , time is : " + LocalDateTime.now().format(formatter)); + + DefaultFuture future = DefaultFuture.getFuture(channelId); + //waiting future should be removed by time out check task + Assertions.assertNull(future); + } /** * mock a default future @@ -125,4 +167,29 @@ private DefaultFuture defaultFuture(int timeout) { return DefaultFuture.newFuture(channel, request, timeout, null); } + /** + * mock a thread interrupt another thread which is waiting waitAndDrain() to return. + */ + static class InterruptThread extends Thread { + private Thread parent; + + public InterruptThread(Thread parent) { + this.parent = parent; + } + + @Override + public void run() { + super.run(); + try { + //interrupt waiting thread before timeout + Thread.sleep(500); + parent.interrupt(); + } catch (InterruptedException e) { + System.out.println(e.getMessage()); + } + } + + } + + } diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java index 5d8c78b82b7..269bc18f3c9 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; @@ -60,7 +61,7 @@ protected Object getField(Object obj, String fieldName, int parentdepth) { clazz = clazz.getSuperclass(); } if (field != null) { - field.setAccessible(true); + ReflectUtils.makeAccessible(field); return field.get(obj); } else { throw new NoSuchFieldException(); @@ -143,4 +144,4 @@ public void received(Channel channel, Object message) throws RemotingException { class BizException extends RuntimeException { private static final long serialVersionUID = -7541893754900723624L; } -} \ No newline at end of file +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.java new file mode 100644 index 00000000000..1822837f346 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/TelnetUtilsTest.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.dubbo.remoting.telnet; + +import org.apache.dubbo.remoting.telnet.support.TelnetUtils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class TelnetUtilsTest { + + /** + * abc - abc - abc + * 1 - 2 - 3 + * x - y - z + */ + @Test + public void testToList() { + List> table = new LinkedList<>(); + table.add(Arrays.asList("abc","abc","abc")); + table.add(Arrays.asList("1","2","3")); + table.add(Arrays.asList("x","y","z")); + + String toList = TelnetUtils.toList(table); + + Assertions.assertTrue(toList.contains("abc - abc - abc")); + Assertions.assertTrue(toList.contains("1 - 2 - 3")); + Assertions.assertTrue(toList.contains("x - y - z")); + } + + /** + * +-----+-----+-----+ + * | A | B | C | + * +-----+-----+-----+ + * | abc | abc | abc | + * | 1 | 2 | 3 | + * | x | y | z | + * +-----+-----+-----+ + */ + @Test + public void testToTable() { + List> table = new LinkedList<>(); + table.add(Arrays.asList("abc","abc","abc")); + table.add(Arrays.asList("1","2","3")); + table.add(Arrays.asList("x","y","z")); + + String toTable = TelnetUtils.toTable(new String[]{"A","B","C"},table); + + Assertions.assertTrue(toTable.contains("| A | B | C |")); + Assertions.assertTrue(toTable.contains("| abc | abc | abc |")); + Assertions.assertTrue(toTable.contains("| 1 | 2 | 3 |")); + Assertions.assertTrue(toTable.contains("| x | y | z |")); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.java new file mode 100644 index 00000000000..7671ab594b7 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ClearTelnetHandlerTest.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.dubbo.remoting.telnet.support; + +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.ClearTelnetHandler; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class ClearTelnetHandlerTest { + + @Test + public void test() { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 50; i++) { + buf.append("\r\n"); + } + + ClearTelnetHandler telnetHandler = new ClearTelnetHandler(); + Assertions.assertEquals(buf.toString(), telnetHandler.telnet(Mockito.mock(Channel.class), "50")); + + // Illegal Input + Assertions.assertTrue(telnetHandler.telnet(Mockito.mock(Channel.class), "Illegal").contains("Illegal")); + + for (int i = 0; i < 50; i++) { + buf.append("\r\n"); + } + Assertions.assertEquals(buf.toString(), telnetHandler.telnet(Mockito.mock(Channel.class), "")); + } +} diff --git a/dubbo-serialization/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/ReflectionUtilsTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java similarity index 58% rename from dubbo-serialization/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/ReflectionUtilsTest.java rename to dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java index ff77d879f9e..470f3106cf2 100644 --- a/dubbo-serialization/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/ReflectionUtilsTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/ExitTelnetHandlerTest.java @@ -14,33 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.dubbo.common.serialize.kryo; +package org.apache.dubbo.remoting.telnet.support; -import org.apache.dubbo.common.serialize.kryo.utils.ReflectionUtils; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.ExitTelnetHandler; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; -public class ReflectionUtilsTest { +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +public class ExitTelnetHandlerTest { @Test public void test() { - assertTrue(ReflectionUtils.checkZeroArgConstructor(String.class)); - assertTrue(ReflectionUtils.checkZeroArgConstructor(Bar.class)); - assertFalse(ReflectionUtils.checkZeroArgConstructor(Foo.class)); - } - - static class Foo { - public Foo(int i) { - - } - } + Channel channel = Mockito.mock(Channel.class); - static class Bar { - private Bar() { + ExitTelnetHandler exitTelnetHandler = new ExitTelnetHandler(); + exitTelnetHandler.telnet(channel, null); - } + verify(channel, times(1)).close(); } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java new file mode 100644 index 00000000000..2f2a10e3e54 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/HelpTelnetHandlerTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.telnet.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.HelpTelnetHandler; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class HelpTelnetHandlerTest { + @Test + public void test() { + Channel channel = Mockito.mock(Channel.class); + Mockito.when(channel.getUrl()).thenReturn(URL.valueOf("dubbo://127.0.0.1:12345")); + + HelpTelnetHandler helpTelnetHandler = new HelpTelnetHandler(); + // default output + String prompt = "Please input \"help [command]\" show detail.\r\n"; + Assertions.assertTrue(helpTelnetHandler.telnet(channel, "").contains(prompt)); + + // "help" command output + String demoOutput = + "Command:\r\n" + + " help [command]\r\n" + + "Summary:\r\n" + + " Show help.\r\n" + + "Detail:\r\n" + + " Show help."; + Assertions.assertEquals(helpTelnetHandler.telnet(channel, "help"),demoOutput); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.java new file mode 100644 index 00000000000..56e884dca11 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/telnet/support/StatusTelnetHandlerTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.telnet.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.telnet.support.command.StatusTelnetHandler; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class StatusTelnetHandlerTest { + @Test + public void test() { + Channel channel = Mockito.mock(Channel.class); + Mockito.when(channel.getUrl()).thenReturn(URL.valueOf("dubbo://127.0.0.1:12345")); + + StatusTelnetHandler statusTelnetHandler = new StatusTelnetHandler(); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"")); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"-l")); + + String errorPrompt = "Unsupported parameter "; + Assertions.assertTrue(statusTelnetHandler.telnet(channel,"other").contains(errorPrompt)); + + Mockito.when(channel.getUrl()).thenReturn(URL.valueOf("dubbo://127.0.0.1:12345?status=load,memory")); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"")); + Assertions.assertNotNull(statusTelnetHandler.telnet(channel,"-l")); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java new file mode 100644 index 00000000000..0e55032006d --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/PayloadDropperTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.remoting.utils; + +import org.apache.dubbo.remoting.exchange.Request; +import org.apache.dubbo.remoting.exchange.Response; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PayloadDropperTest { + @Test + public void test() { + Request request = new Request(1); + request.setData(new Object()); + Request requestWithoutData = (Request) PayloadDropper.getRequestWithoutData(request); + Assertions.assertEquals(requestWithoutData.getId(), request.getId()); + Assertions.assertNull(requestWithoutData.getData()); + + Response response = new Response(1); + response.setResult(new Object()); + Response responseWithoutData = (Response) PayloadDropper.getRequestWithoutData(response); + Assertions.assertEquals(responseWithoutData.getId(), response.getId()); + Assertions.assertNull(responseWithoutData.getResult()); + + Object object = new Object(); + Assertions.assertEquals(object, PayloadDropper.getRequestWithoutData(object)); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.java new file mode 100644 index 00000000000..7b475aecc8e --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/utils/UrlUtilsTest.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.dubbo.remoting.utils; + +import org.apache.dubbo.common.URL; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UrlUtilsTest { + @Test + public void testGetIdleTimeout() { + URL url1 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000"); + URL url2 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000&heartbeat.timeout=50000"); + URL url3 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000&heartbeat.timeout=10000"); + Assertions.assertEquals(UrlUtils.getIdleTimeout(url1), 30000); + Assertions.assertEquals(UrlUtils.getIdleTimeout(url2), 50000); + Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getIdleTimeout(url3)); + } + + @Test + public void testGetHeartbeat() { + URL url = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000"); + Assertions.assertEquals(UrlUtils.getHeartbeat(url), 10000); + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter new file mode 100644 index 00000000000..8c02a4a3c16 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter @@ -0,0 +1 @@ +mockTransporter = org.apache.dubbo.remoting.MockTransporter \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger new file mode 100644 index 00000000000..f5b1c5687de --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger @@ -0,0 +1 @@ +mockExchanger = org.apache.dubbo.remoting.exchange.MockExchanger \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/EtcdClient.java b/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/EtcdClient.java index 7dc20ff34ce..45b54b00a17 100644 --- a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/EtcdClient.java +++ b/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/EtcdClient.java @@ -81,7 +81,7 @@ public interface EtcdClient { * register children listener for specified path. * * @param path the path to be watched when children is added, delete or update. - * @param listener when children is changed , listener will be trigged. + * @param listener when children is changed , listener will be triggered. * @return direct children directory, contains zero element * list if children directory not exists. */ @@ -91,7 +91,7 @@ public interface EtcdClient { * find watcher of the children listener for specified path. * * @param path the path to be watched when children is added, delete or update. - * @param listener when children is changed , listener will be trigged. + * @param listener when children is changed , listener will be triggered. * @return watcher if find else null */ T getChildListener(String path, ChildListener listener); @@ -100,7 +100,7 @@ public interface EtcdClient { * unregister children lister for specified path. * * @param path the path to be unwatched . - * @param listener when children is changed , lister will be trigged. + * @param listener when children is changed , lister will be triggered. */ void removeChildListener(String path, ChildListener listener); diff --git a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientWrapper.java b/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientWrapper.java index d6c57337ad7..380bb976b4e 100644 --- a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientWrapper.java +++ b/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientWrapper.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.NamedThreadFactory; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.etcd.RetryPolicy; import org.apache.dubbo.remoting.etcd.StateListener; @@ -47,6 +48,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -112,7 +114,7 @@ public class JEtcdClientWrapper { private volatile boolean cancelKeepAlive = false; - public static final Charset UTF_8 = Charset.forName("UTF-8"); + public static final Charset UTF_8 = StandardCharsets.UTF_8; public JEtcdClientWrapper(URL url) { this.url = url; @@ -498,7 +500,7 @@ public String[] endPoints(String backupAddress) { /** * because jetcd's connection change callback not supported yet, we must - * loop to test if connect or disconnect event happend or not. It will be changed + * loop to test if connect or disconnect event happened or not. It will be changed * in the future if we found better choice. */ public void start() { @@ -594,7 +596,7 @@ protected void doClose() { } /** - * try get client's shared channel, becase all fields is private on jetcd, + * try get client's shared channel, because all fields is private on jetcd, * we must using it by reflect, in the future, jetcd may provider better tools. * * @param client get channel from current client @@ -603,14 +605,10 @@ protected void doClose() { private ManagedChannel newChannel(Client client) { try { Field connectionField = client.getClass().getDeclaredField("connectionManager"); - if (!connectionField.isAccessible()) { - connectionField.setAccessible(true); - } + ReflectUtils.makeAccessible(connectionField); Object connection = connectionField.get(client); Method channel = connection.getClass().getDeclaredMethod("getChannel"); - if (!channel.isAccessible()) { - channel.setAccessible(true); - } + ReflectUtils.makeAccessible(channel); return (ManagedChannel) channel.invoke(connection); } catch (Exception e) { throw new RuntimeException("Failed to obtain connection channel from " + url.getBackupAddress(), e); @@ -625,9 +623,9 @@ public void setConnectionStateListener(ConnectionStateListener connectionStateLi this.connectionStateListener = connectionStateListener; } - public static void requiredNotNull(Object obj, RuntimeException exeception) { + public static void requiredNotNull(Object obj, RuntimeException exception) { if (obj == null) { - throw exeception; + throw exception; } } diff --git a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/option/OptionUtil.java b/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/option/OptionUtil.java index 5cb13fc726b..fa5955cdebe 100644 --- a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/option/OptionUtil.java +++ b/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/option/OptionUtil.java @@ -20,21 +20,16 @@ import io.grpc.Status; import io.netty.handler.codec.http2.Http2Exception; -import java.util.Arrays; - public class OptionUtil { public static final byte[] NO_PREFIX_END = {0}; public static final ByteSequence prefixEndOf(ByteSequence prefix) { byte[] endKey = prefix.getBytes().clone(); - for (int i = endKey.length - 1; i >= 0; i--) { - if (endKey[i] < 0xff) { - endKey[i] = (byte) (endKey[i] + 1); - return ByteSequence.from(Arrays.copyOf(endKey, i + 1)); - } + if (prefix.size() > 0) { + endKey[endKey.length - 1] = (byte) (endKey[endKey.length - 1] + 1); + return ByteSequence.from(endKey); } - return ByteSequence.from(NO_PREFIX_END); } diff --git a/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientTest.java b/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientTest.java index 9f91a82144c..5b222d8bbb3 100644 --- a/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientTest.java +++ b/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdClientTest.java @@ -34,6 +34,7 @@ package org.apache.dubbo.remoting.etcd.jetcd; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.remoting.etcd.ChildListener; import com.google.protobuf.ByteString; @@ -414,10 +415,10 @@ private ManagedChannel getChannel(Client client) { try { // hack, use reflection to get the shared channel. Field connectionField = client.getClass().getDeclaredField("connectionManager"); - connectionField.setAccessible(true); + ReflectUtils.makeAccessible(connectionField); Object connection = connectionField.get(client); Method channelMethod = connection.getClass().getDeclaredMethod("getChannel"); - channelMethod.setAccessible(true); + ReflectUtils.makeAccessible(channelMethod); ManagedChannel channel = (ManagedChannel) channelMethod.invoke(connection); return channel; } catch (Exception e) { diff --git a/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java b/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java index 898e1a9728f..6188984878a 100644 --- a/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java +++ b/dubbo-remoting/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java @@ -49,6 +49,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; @@ -61,6 +62,7 @@ /** * @author cvictory ON 2019-08-16 */ +@Disabled public class LeaseTest { private static EtcdCluster cluster; diff --git a/dubbo-remoting/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java b/dubbo-remoting/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java index 39e6990c195..c720fd5693a 100644 --- a/dubbo-remoting/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java +++ b/dubbo-remoting/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java @@ -128,10 +128,8 @@ public NextAction handleRead(FilterChainContext context) throws IOException { } if (msg != null) { context.setMessage(msg); - return context.getInvokeAction(); - } else { - return context.getInvokeAction(); } + return context.getInvokeAction(); } } while (frame.readable()); } else { // Other events are passed down directly diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java index 001b56ad2a4..3783d024b52 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java @@ -39,7 +39,7 @@ public class ThreadNameTest { private ThreadNameVerifyHandler clientHandler; private static String serverRegex = "DubboServerHandler\\-localhost:(\\d+)\\-thread\\-(\\d+)"; - private static String clientRegex = "DubboClientHandler\\-localhost:(\\d+)\\-thread\\-(\\d+)"; + private static String clientRegex = "DubboClientHandler\\-thread\\-(\\d+)"; @BeforeEach public void before() throws Exception { diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java index b301a784e5b..f9369fdd022 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java @@ -40,14 +40,16 @@ import io.netty.handler.timeout.IdleStateHandler; import java.net.InetSocketAddress; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.Map; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.KEEP_ALIVE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SSL_ENABLED_KEY; + /** * NettyServer. */ @@ -94,10 +96,13 @@ protected void doOpen() throws Throwable { final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this); channels = nettyServerHandler.getChannels(); + boolean keepalive = getUrl().getParameter(KEEP_ALIVE_KEY, Boolean.FALSE); + bootstrap.group(bossGroup, workerGroup) .channel(NettyEventLoopFactory.serverSocketChannelClass()) .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE) .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE) + .childOption(ChannelOption.SO_KEEPALIVE, keepalive) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .childHandler(new ChannelInitializer() { @Override @@ -166,14 +171,16 @@ protected void doClose() throws Throwable { @Override public Collection getChannels() { - Collection chs = new HashSet(); - for (Channel channel : this.channels.values()) { - if (channel.isConnected()) { - chs.add(channel); - } else { - channels.remove(NetUtils.toAddressString(channel.getRemoteAddress())); - } - } + Collection chs = new ArrayList<>(this.channels.size()); + chs.addAll(this.channels.values()); + // check of connection status is unnecessary since we are using channels in NettyServerHandler +// for (Channel channel : this.channels.values()) { +// if (channel.isConnected()) { +// chs.add(channel); +// } else { +// channels.remove(NetUtils.toAddressString(channel.getRemoteAddress())); +// } +// } return chs; } diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/logging/MessageFormatter.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/logging/MessageFormatter.java index 808600e97f6..26eb8c1041b 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/logging/MessageFormatter.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/logging/MessageFormatter.java @@ -197,7 +197,7 @@ static FormattingTuple arrayFormat(final String messagePattern, throwableCandidate); } } else { - if (isEscapedDelimeter(messagePattern, j)) { + if (isEscapedDelimiter(messagePattern, j)) { if (!isDoubleEscaped(messagePattern, j)) { l--; // DELIM_START was escaped, thus should not be incremented sbuf.append(messagePattern, i, j - 1); @@ -228,18 +228,18 @@ static FormattingTuple arrayFormat(final String messagePattern, } } - static boolean isEscapedDelimeter(String messagePattern, - int delimeterStartIndex) { + static boolean isEscapedDelimiter(String messagePattern, + int delimiterStartIndex) { - if (delimeterStartIndex == 0) { + if (delimiterStartIndex == 0) { return false; } - return messagePattern.charAt(delimeterStartIndex - 1) == ESCAPE_CHAR; + return messagePattern.charAt(delimiterStartIndex - 1) == ESCAPE_CHAR; } static boolean isDoubleEscaped(String messagePattern, - int delimeterStartIndex) { - return delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR; + int delimiterStartIndex) { + return delimiterStartIndex >= 2 && messagePattern.charAt(delimiterStartIndex - 2) == ESCAPE_CHAR; } // special treatment of array values was suggested by 'lizongbo' diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java index e8080867740..83a525aa389 100644 --- a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java +++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java @@ -37,9 +37,9 @@ public class MulticastExchangeGroup extends AbstractExchangeGroup { private static final String LEAVE = "leave"; - private InetAddress mutilcastAddress; + private InetAddress multicastAddress; - private MulticastSocket mutilcastSocket; + private MulticastSocket multicastSocket; public MulticastExchangeGroup(URL url) { super(url); @@ -47,10 +47,10 @@ public MulticastExchangeGroup(URL url) { throw new IllegalArgumentException("Invalid multicast address " + url.getHost() + ", scope: 224.0.0.0 - 239.255.255.255"); } try { - mutilcastAddress = InetAddress.getByName(url.getHost()); - mutilcastSocket = new MulticastSocket(url.getPort()); - mutilcastSocket.setLoopbackMode(false); - mutilcastSocket.joinGroup(mutilcastAddress); + multicastAddress = InetAddress.getByName(url.getHost()); + multicastSocket = new MulticastSocket(url.getPort()); + multicastSocket.setLoopbackMode(false); + multicastSocket.joinGroup(multicastAddress); Thread thread = new Thread(new Runnable() { @Override public void run() { @@ -58,7 +58,7 @@ public void run() { DatagramPacket recv = new DatagramPacket(buf, buf.length); while (true) { try { - mutilcastSocket.receive(recv); + multicastSocket.receive(recv); MulticastExchangeGroup.this.receive(new String(recv.getData()).trim(), (InetSocketAddress) recv.getSocketAddress()); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -74,9 +74,9 @@ public void run() { } private void send(String msg) throws RemotingException { - DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), mutilcastAddress, mutilcastSocket.getLocalPort()); + DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), multicastAddress, multicastSocket.getLocalPort()); try { - mutilcastSocket.send(hi); + multicastSocket.send(hi); } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } diff --git a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/support/MulticastGroup.java b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/support/MulticastGroup.java index e070d106143..1df7e9faf87 100644 --- a/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/support/MulticastGroup.java +++ b/dubbo-remoting/dubbo-remoting-p2p/src/main/java/org/apache/dubbo/remoting/p2p/support/MulticastGroup.java @@ -37,9 +37,9 @@ public class MulticastGroup extends AbstractGroup { private static final String LEAVE = "leave"; - private InetAddress mutilcastAddress; + private InetAddress multicastAddress; - private MulticastSocket mutilcastSocket; + private MulticastSocket multicastSocket; public MulticastGroup(URL url) { super(url); @@ -47,10 +47,10 @@ public MulticastGroup(URL url) { throw new IllegalArgumentException("Invalid multicast address " + url.getHost() + ", scope: 224.0.0.0 - 239.255.255.255"); } try { - mutilcastAddress = InetAddress.getByName(url.getHost()); - mutilcastSocket = new MulticastSocket(url.getPort()); - mutilcastSocket.setLoopbackMode(false); - mutilcastSocket.joinGroup(mutilcastAddress); + multicastAddress = InetAddress.getByName(url.getHost()); + multicastSocket = new MulticastSocket(url.getPort()); + multicastSocket.setLoopbackMode(false); + multicastSocket.joinGroup(multicastAddress); Thread thread = new Thread(new Runnable() { @Override public void run() { @@ -58,7 +58,7 @@ public void run() { DatagramPacket recv = new DatagramPacket(buf, buf.length); while (true) { try { - mutilcastSocket.receive(recv); + multicastSocket.receive(recv); MulticastGroup.this.receive(new String(recv.getData()).trim(), (InetSocketAddress) recv.getSocketAddress()); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -74,9 +74,9 @@ public void run() { } private void send(String msg) throws RemotingException { - DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), mutilcastAddress, mutilcastSocket.getLocalPort()); + DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), multicastAddress, multicastSocket.getLocalPort()); try { - mutilcastSocket.send(hi); + multicastSocket.send(hi); } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java index cbb37479cd3..775d2910c71 100644 --- a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java +++ b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java @@ -62,4 +62,6 @@ public interface ZookeeperClient { String getContent(String path); + boolean checkExists(String path); + } diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java index 625f00176d4..1971017af6f 100644 --- a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java +++ b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java @@ -28,9 +28,9 @@ import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.CuratorWatcher; -import org.apache.curator.framework.recipes.cache.TreeCache; -import org.apache.curator.framework.recipes.cache.TreeCacheEvent; -import org.apache.curator.framework.recipes.cache.TreeCacheListener; +import org.apache.curator.framework.recipes.cache.ChildData; +import org.apache.curator.framework.recipes.cache.NodeCache; +import org.apache.curator.framework.recipes.cache.NodeCacheListener; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryNTimes; @@ -41,6 +41,7 @@ import org.apache.zookeeper.Watcher; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -49,14 +50,14 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; -public class CuratorZookeeperClient extends AbstractZookeeperClient { +public class CuratorZookeeperClient extends AbstractZookeeperClient { protected static final Logger logger = LoggerFactory.getLogger(CuratorZookeeperClient.class); private static final String ZK_SESSION_EXPIRE_KEY = "zk.session.expire"; - static final Charset CHARSET = Charset.forName("UTF-8"); + static final Charset CHARSET = StandardCharsets.UTF_8; private final CuratorFramework client; - private Map treeCacheMap = new ConcurrentHashMap<>(); + private static Map nodeCacheMap = new ConcurrentHashMap<>(); public CuratorZookeeperClient(URL url) { super(url); @@ -148,7 +149,7 @@ protected void createEphemeral(String path, String data) { protected void deletePath(String path) { try { client.delete().deletingChildrenIfNeeded().forPath(path); - } catch (NoNodeException e) { + } catch (NoNodeException ignored) { } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } @@ -216,40 +217,41 @@ public List addTargetChildListener(String path, CuratorWatcherImpl liste } @Override - protected CuratorZookeeperClient.CuratorWatcherImpl createTargetDataListener(String path, DataListener listener) { - return new CuratorWatcherImpl(client, listener); + protected CuratorZookeeperClient.NodeCacheListenerImpl createTargetDataListener(String path, DataListener listener) { + return new NodeCacheListenerImpl(client, listener, path); } @Override - protected void addTargetDataListener(String path, CuratorZookeeperClient.CuratorWatcherImpl treeCacheListener) { - this.addTargetDataListener(path, treeCacheListener, null); + protected void addTargetDataListener(String path, CuratorZookeeperClient.NodeCacheListenerImpl nodeCacheListener) { + this.addTargetDataListener(path, nodeCacheListener, null); } @Override - protected void addTargetDataListener(String path, CuratorZookeeperClient.CuratorWatcherImpl treeCacheListener, Executor executor) { + protected void addTargetDataListener(String path, CuratorZookeeperClient.NodeCacheListenerImpl nodeCacheListener, Executor executor) { try { - TreeCache treeCache = TreeCache.newBuilder(client, path).setCacheData(false).build(); - treeCacheMap.putIfAbsent(path, treeCache); - + NodeCache nodeCache = new NodeCache(client, path); + if (nodeCacheMap.putIfAbsent(path, nodeCache) != null) { + return; + } if (executor == null) { - treeCache.getListenable().addListener(treeCacheListener); + nodeCache.getListenable().addListener(nodeCacheListener); } else { - treeCache.getListenable().addListener(treeCacheListener, executor); + nodeCache.getListenable().addListener(nodeCacheListener, executor); } - treeCache.start(); + nodeCache.start(); } catch (Exception e) { - throw new IllegalStateException("Add treeCache listener for path:" + path, e); + throw new IllegalStateException("Add nodeCache listener for path:" + path, e); } } @Override - protected void removeTargetDataListener(String path, CuratorZookeeperClient.CuratorWatcherImpl treeCacheListener) { - TreeCache treeCache = treeCacheMap.get(path); - if (treeCache != null) { - treeCache.getListenable().removeListener(treeCacheListener); + protected void removeTargetDataListener(String path, CuratorZookeeperClient.NodeCacheListenerImpl nodeCacheListener) { + NodeCache nodeCache = nodeCacheMap.get(path); + if (nodeCache != null) { + nodeCache.getListenable().removeListener(nodeCacheListener); } - treeCacheListener.dataListener = null; + nodeCacheListener.dataListener = null; } @Override @@ -257,21 +259,48 @@ public void removeTargetChildListener(String path, CuratorWatcherImpl listener) listener.unwatch(); } - static class CuratorWatcherImpl implements CuratorWatcher, TreeCacheListener { + static class NodeCacheListenerImpl implements NodeCacheListener { private CuratorFramework client; - private volatile ChildListener childListener; + private volatile DataListener dataListener; + private String path; - public CuratorWatcherImpl(CuratorFramework client, ChildListener listener, String path) { + protected NodeCacheListenerImpl() { + } + + public NodeCacheListenerImpl(CuratorFramework client, DataListener dataListener, String path) { this.client = client; - this.childListener = listener; + this.dataListener = dataListener; this.path = path; } - public CuratorWatcherImpl(CuratorFramework client, DataListener dataListener) { - this.dataListener = dataListener; + @Override + public void nodeChanged() throws Exception { + ChildData childData = nodeCacheMap.get(path).getCurrentData(); + String content = null; + EventType eventType; + if (childData == null) { + eventType = EventType.NodeDeleted; + } else { + content = new String(childData.getData(), CHARSET); + eventType = EventType.NodeDataChanged; + } + dataListener.dataChanged(path, content, eventType); + } + } + + static class CuratorWatcherImpl implements CuratorWatcher { + + private CuratorFramework client; + private volatile ChildListener childListener; + private String path; + + public CuratorWatcherImpl(CuratorFramework client, ChildListener listener, String path) { + this.client = client; + this.childListener = listener; + this.path = path; } protected CuratorWatcherImpl() { @@ -293,66 +322,22 @@ public void process(WatchedEvent event) throws Exception { childListener.childChanged(path, client.getChildren().usingWatcher(this).forPath(path)); } } - - @Override - public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { - if (dataListener != null) { - if (logger.isDebugEnabled()) { - logger.debug("listen the zookeeper changed. The changed data:" + event.getData()); - } - TreeCacheEvent.Type type = event.getType(); - EventType eventType = null; - String content = null; - String path = null; - switch (type) { - case NODE_ADDED: - eventType = EventType.NodeCreated; - path = event.getData().getPath(); - content = event.getData().getData() == null ? "" : new String(event.getData().getData(), CHARSET); - break; - case NODE_UPDATED: - eventType = EventType.NodeDataChanged; - path = event.getData().getPath(); - content = event.getData().getData() == null ? "" : new String(event.getData().getData(), CHARSET); - break; - case NODE_REMOVED: - path = event.getData().getPath(); - eventType = EventType.NodeDeleted; - break; - case INITIALIZED: - eventType = EventType.INITIALIZED; - break; - case CONNECTION_LOST: - eventType = EventType.CONNECTION_LOST; - break; - case CONNECTION_RECONNECTED: - eventType = EventType.CONNECTION_RECONNECTED; - break; - case CONNECTION_SUSPENDED: - eventType = EventType.CONNECTION_SUSPENDED; - break; - - } - dataListener.dataChanged(path, content, eventType); - } - } } private class CuratorConnectionStateListener implements ConnectionStateListener { private final long UNKNOWN_SESSION_ID = -1L; private long lastSessionId; - private URL url; + private int timeout; + private int sessionExpireMs; public CuratorConnectionStateListener(URL url) { - this.url = url; + this.timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_CONNECTION_TIMEOUT_MS); + this.sessionExpireMs = url.getParameter(ZK_SESSION_EXPIRE_KEY, DEFAULT_SESSION_TIMEOUT_MS); } @Override public void stateChanged(CuratorFramework client, ConnectionState state) { - int timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_CONNECTION_TIMEOUT_MS); - int sessionExpireMs = url.getParameter(ZK_SESSION_EXPIRE_KEY, DEFAULT_SESSION_TIMEOUT_MS); - long sessionId = UNKNOWN_SESSION_ID; try { sessionId = client.getZookeeperClient().getZooKeeper().getSessionId(); diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperClient.java b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperClient.java index 6b7f8e2201e..8ee4b4bbff4 100644 --- a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperClient.java +++ b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperClient.java @@ -49,7 +49,7 @@ public abstract class AbstractZookeeperClient persistentExistNodePath = new ConcurrentHashSet<>(); + private final Set persistentExistNodePath = new ConcurrentHashSet<>(); public AbstractZookeeperClient(URL url) { this.url = url; @@ -61,7 +61,7 @@ public URL getUrl() { } @Override - public void delete(String path){ + public void delete(String path) { //never mind if ephemeral persistentExistNodePath.remove(path); deletePath(path); @@ -71,7 +71,7 @@ public void delete(String path){ @Override public void create(String path, boolean ephemeral) { if (!ephemeral) { - if(persistentExistNodePath.contains(path)){ + if (persistentExistNodePath.contains(path)) { return; } if (checkExists(path)) { @@ -125,11 +125,11 @@ public void addDataListener(String path, DataListener listener, Executor executo } @Override - public void removeDataListener(String path, DataListener listener ){ + public void removeDataListener(String path, DataListener listener) { ConcurrentMap dataListenerMap = listeners.get(path); if (dataListenerMap != null) { TargetDataListener targetListener = dataListenerMap.remove(listener); - if(targetListener != null){ + if (targetListener != null) { removeTargetDataListener(path, targetListener); } } @@ -199,7 +199,7 @@ public String getContent(String path) { protected abstract void createEphemeral(String path, String data); - protected abstract boolean checkExists(String path); + public abstract boolean checkExists(String path); protected abstract TargetChildListener createTargetChildListener(String path, ChildListener listener); @@ -219,6 +219,7 @@ public String getContent(String path) { /** * we invoke the zookeeper client to delete the node + * * @param path the node path */ protected abstract void deletePath(String path); diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperTransporter.java b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperTransporter.java index b861c159152..821f4c5ea16 100644 --- a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperTransporter.java +++ b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/support/AbstractZookeeperTransporter.java @@ -43,7 +43,7 @@ public abstract class AbstractZookeeperTransporter implements ZookeeperTransport private final Map zookeeperClientMap = new ConcurrentHashMap<>(); /** - * share connnect for registry, metadata, etc.. + * share connect for registry, metadata, etc.. *

    * Make sure the connection is connected. * diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/src/test/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClientTest.java b/dubbo-remoting/dubbo-remoting-zookeeper/src/test/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClientTest.java index 2f2b2f58680..aa37cbebf43 100644 --- a/dubbo-remoting/dubbo-remoting-zookeeper/src/test/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClientTest.java +++ b/dubbo-remoting/dubbo-remoting-zookeeper/src/test/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClientTest.java @@ -22,7 +22,6 @@ import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import org.apache.zookeeper.WatchedEvent; @@ -172,24 +171,25 @@ public void testAddTargetDataListener() throws Exception { String valueFromCache = curatorClient.getContent(path + "/d.json"); Assertions.assertEquals(value, valueFromCache); final AtomicInteger atomicInteger = new AtomicInteger(0); - curatorClient.addTargetDataListener(listenerPath, new CuratorZookeeperClient.CuratorWatcherImpl() { + curatorClient.addTargetDataListener(path + "/d.json", new CuratorZookeeperClient.NodeCacheListenerImpl() { + @Override - public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { - System.out.println("===" + event); + public void nodeChanged() throws Exception { atomicInteger.incrementAndGet(); } }); valueFromCache = curatorClient.getContent(path + "/d.json"); Assertions.assertNotNull(valueFromCache); - curatorClient.getClient().setData().forPath(path + "/d.json", "sdsdf".getBytes()); - curatorClient.getClient().setData().forPath(path + "/d.json", "dfsasf".getBytes()); + + Thread.sleep(100); + curatorClient.getClient().setData().forPath(path + "/d.json", "foo".getBytes()); + Thread.sleep(100); + curatorClient.getClient().setData().forPath(path + "/d.json", "bar".getBytes()); curatorClient.delete(path + "/d.json"); - curatorClient.delete(path); valueFromCache = curatorClient.getContent(path + "/d.json"); Assertions.assertNull(valueFromCache); Thread.sleep(2000L); - Assertions.assertTrue(9L >= atomicInteger.get()); - Assertions.assertTrue(2L <= atomicInteger.get()); + Assertions.assertTrue(3L <= atomicInteger.get()); } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java index 7ebff3fb4e7..27df08acdc4 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java @@ -16,7 +16,9 @@ */ package org.apache.dubbo.rpc; -import java.lang.reflect.Field; + +import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler; + import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -26,13 +28,15 @@ import java.util.function.BiConsumer; import java.util.function.Function; +import static org.apache.dubbo.rpc.Constants.INVOCATION_KEY; + /** * {@link AsyncRpcResult} is introduced in 3.0.0 to replace RpcResult, and RpcResult is replaced with {@link AppResponse}: *

      *
    • AsyncRpcResult is the object that is actually passed in the call chain
    • *
    • AppResponse only simply represents the business result
    • *
    - * + *

    * The relationship between them can be described as follow, an abstraction of the definition of AsyncRpcResult: *

      *  {@code
    @@ -41,7 +45,7 @@
      *  }
      * 
    * AsyncRpcResult is a future representing an unfinished RPC call, while AppResponse is the actual return type of this call. - * In theory, AppResponse does'n have to implement the {@link Result} interface, this is done mainly for compatibility purpose. + * In theory, AppResponse doesn't have to implement the {@link Result} interface, this is done mainly for compatibility purpose. * * @serial Do not change the class name and properties. */ @@ -55,9 +59,15 @@ public class AppResponse implements Result { private Map attachments = new HashMap<>(); + private Map attributes = new HashMap<>(); + public AppResponse() { } + public AppResponse(Invocation invocation) { + this.setAttribute(INVOCATION_KEY, invocation); + } + public AppResponse(Object result) { this.result = result; } @@ -71,15 +81,7 @@ public Object recreate() throws Throwable { if (exception != null) { // fix issue#619 try { - // get Throwable class - Class clazz = exception.getClass(); - while (!clazz.getName().equals(Throwable.class.getName())) { - clazz = clazz.getSuperclass(); - } - // get stackTrace value - Field stackTraceField = clazz.getDeclaredField("stackTrace"); - stackTraceField.setAccessible(true); - Object stackTrace = stackTraceField.get(exception); + Object stackTrace = InvokerInvocationHandler.stackTraceField.get(exception); if (stackTrace == null) { exception.setStackTrace(new StackTraceElement[0]); } @@ -212,6 +214,14 @@ public void setObjectAttachment(String key, Object value) { attachments.put(key, value); } + public Object getAttribute(String key) { + return attributes.get(key); + } + + public void setAttribute(String key, Object value) { + attributes.put(key, value); + } + @Override public Result whenCompleteWithContext(BiConsumer fn) { throw new UnsupportedOperationException("AppResponse represents an concrete business response, there will be no status changes, you should get internal values directly."); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java index d61d48fdeca..0db0f196a04 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java @@ -94,7 +94,7 @@ public void setValue(Object value) { if (responseFuture.isDone()) { responseFuture.get().setValue(value); } else { - AppResponse appResponse = new AppResponse(); + AppResponse appResponse = new AppResponse(invocation); appResponse.setValue(value); responseFuture.complete(appResponse); } @@ -116,7 +116,7 @@ public void setException(Throwable t) { if (responseFuture.isDone()) { responseFuture.get().setException(t); } else { - AppResponse appResponse = new AppResponse(); + AppResponse appResponse = new AppResponse(invocation); appResponse.setException(t); responseFuture.complete(appResponse); } @@ -319,7 +319,7 @@ public static AsyncRpcResult newDefaultAsyncResult(Throwable t, Invocation invoc public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) { CompletableFuture future = new CompletableFuture<>(); - AppResponse result = new AppResponse(); + AppResponse result = new AppResponse(invocation); if (t != null) { result.setException(t); } else { diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java index 1703792cab8..def349a7530 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AttachmentsAdapter.java @@ -30,9 +30,9 @@ public static class ObjectToStringMap extends HashMap { public ObjectToStringMap(Map attachments) { for (Entry entry : attachments.entrySet()) { - String converResult = convert(entry.getValue()); - if (converResult != null) { - super.put(entry.getKey(), converResult); + String convertResult = convert(entry.getValue()); + if (convertResult != null) { + super.put(entry.getKey(), convertResult); } } this.attachments = attachments; diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java index f5fd98278b2..a544fa44e00 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Constants.java @@ -90,4 +90,8 @@ public interface Constants { String CONSUMER_MODEL = "consumerModel"; String METHOD_MODEL = "methodModel"; + + String SERIALIZATION_SECURITY_CHECK_KEY = "serialization.security.check"; + String INVOCATION_KEY = "invocation"; + String SERIALIZATION_ID_KEY = "serialization_id"; } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcException.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcException.java index a95165a0fbd..3a5b1f37d9b 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcException.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcException.java @@ -91,7 +91,7 @@ public boolean isBiz() { return code == BIZ_EXCEPTION; } - public boolean isForbidded() { + public boolean isForbidden() { return code == FORBIDDEN_EXCEPTION; } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java index 3333f0ff93b..093baf581df 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcStatus.java @@ -32,10 +32,14 @@ */ public class RpcStatus { - private static final ConcurrentMap SERVICE_STATISTICS = new ConcurrentHashMap(); + private static final ConcurrentMap SERVICE_STATISTICS = new ConcurrentHashMap(); + + private static final ConcurrentMap> METHOD_STATISTICS = + new ConcurrentHashMap>(); - private static final ConcurrentMap> METHOD_STATISTICS = new ConcurrentHashMap>(); private final ConcurrentMap values = new ConcurrentHashMap(); + private final AtomicInteger active = new AtomicInteger(); private final AtomicLong total = new AtomicLong(); private final AtomicInteger failed = new AtomicInteger(); @@ -103,14 +107,18 @@ public static boolean beginCount(URL url, String methodName, int max) { } for (int i; ; ) { i = methodStatus.active.get(); - if (i + 1 > max) { + + if (i == Integer.MAX_VALUE || i + 1 > max) { return false; } + if (methodStatus.active.compareAndSet(i, i + 1)) { break; } } + appStatus.active.incrementAndGet(); + return true; } @@ -128,13 +136,16 @@ private static void endCount(RpcStatus status, long elapsed, boolean succeeded) status.active.decrementAndGet(); status.total.incrementAndGet(); status.totalElapsed.addAndGet(elapsed); + if (status.maxElapsed.get() < elapsed) { status.maxElapsed.set(elapsed); } + if (succeeded) { if (status.succeededMaxElapsed.get() < elapsed) { status.succeededMaxElapsed.set(elapsed); } + } else { status.failed.incrementAndGet(); status.failedElapsed.addAndGet(elapsed); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java index 63de8c464ba..2c451c6ff52 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java @@ -43,9 +43,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; -import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.Constants.ACCESS_LOG_KEY; /** @@ -103,7 +101,8 @@ public Result invoke(Invoker invoker, Invocation inv) throws RpcException { try { String accessLogKey = invoker.getUrl().getParameter(ACCESS_LOG_KEY); if (ConfigUtils.isNotEmpty(accessLogKey)) { - AccessLogData logData = buildAccessLogData(invoker, inv); + AccessLogData logData = AccessLogData.newLogData(); + logData.buildAccessLogData(invoker, inv); log(accessLogKey, logData); } } catch (Throwable t) { @@ -166,18 +165,6 @@ private void processWithAccessKeyLogger(Set logSet, File file) th } } - private AccessLogData buildAccessLogData(Invoker invoker, Invocation inv) { - AccessLogData logData = AccessLogData.newLogData(); - logData.setServiceName(invoker.getInterface().getName()); - logData.setMethodName(inv.getMethodName()); - logData.setVersion(invoker.getUrl().getParameter(VERSION_KEY)); - logData.setGroup(invoker.getUrl().getParameter(GROUP_KEY)); - logData.setInvocationTime(new Date()); - logData.setTypes(inv.getParameterTypes()); - logData.setArguments(inv.getArguments()); - return logData; - } - private void processWithServiceLogger(Set logSet) { for (Iterator iterator = logSet.iterator(); iterator.hasNext(); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java index 291601aa42b..52b97e704d6 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java @@ -55,7 +55,7 @@ * * @see RpcContext */ -@Activate(group = PROVIDER, order = -10000) +@Activate(group = PROVIDER, order = Integer.MIN_VALUE) public class ContextFilter implements Filter, Filter.Listener { private static final String TAG_KEY = "dubbo.tag"; diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java index f4496440e76..73913174a7c 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ExceptionFilter.java @@ -65,8 +65,8 @@ public void onResponse(Result appResponse, Invoker invoker, Invocation invoca // directly throw if the exception appears in the signature try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); - Class[] exceptionClassses = method.getExceptionTypes(); - for (Class exceptionClass : exceptionClassses) { + Class[] exceptionClasses = method.getExceptionTypes(); + for (Class exceptionClass : exceptionClasses) { if (exception.getClass().equals(exceptionClass)) { return; } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java index 0234139e2bc..2e6d3a8f253 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java @@ -19,11 +19,14 @@ import org.apache.dubbo.common.beanutil.JavaBeanAccessor; import org.apache.dubbo.common.beanutil.JavaBeanDescriptor; import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil; +import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.io.UnsafeByteArrayInputStream; import org.apache.dubbo.common.io.UnsafeByteArrayOutputStream; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Serialization; import org.apache.dubbo.common.utils.PojoUtils; import org.apache.dubbo.common.utils.ReflectUtils; @@ -35,6 +38,7 @@ import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.support.ProtocolUtils; @@ -54,6 +58,7 @@ */ @Activate(group = CommonConstants.PROVIDER, order = -20000) public class GenericFilter implements Filter, Filter.Listener { + private static final Logger logger = LoggerFactory.getLogger(GenericFilter.class); @Override public Result invoke(Invoker invoker, Invocation inv) throws RpcException { @@ -71,8 +76,13 @@ public Result invoke(Invoker invoker, Invocation inv) throws RpcException { args = new Object[params.length]; } + if (types == null) { + types = new String[params.length]; + } + if (args.length != types.length) { - throw new RpcException("args.length != types.length"); + throw new RpcException("GenericFilter#invoke args.length != types.length, please check your " + + "params"); } String generic = inv.getAttachment(GENERIC_KEY); @@ -85,6 +95,18 @@ public Result invoke(Invoker invoker, Invocation inv) throws RpcException { || ProtocolUtils.isGenericReturnRawResult(generic)) { args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { + Configuration configuration = ApplicationModel.getEnvironment().getConfiguration(); + if (!configuration.getBoolean(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, false)) { + String notice = "Trigger the safety barrier! " + + "Native Java Serializer is not allowed by default." + + "This means currently maybe being attacking by others. " + + "If you are sure this is a mistake, " + + "please set `" + CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE + "` enable in configuration! " + + "Before doing so, please make sure you have configure JEP290 to prevent serialization attack."; + logger.error(notice); + throw new RpcException(new IllegalStateException(notice)); + } + for (int i = 0; i < args.length; i++) { if (byte[].class == args[i].getClass()) { try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) { @@ -200,7 +222,7 @@ public void onResponse(Result appResponse, Invoker invoker, Invocation inv) { GENERIC_SERIALIZATION_PROTOBUF + "] serialize result failed.", e); } - } else if(ProtocolUtils.isGenericReturnRawResult(generic)) { + } else if (ProtocolUtils.isGenericReturnRawResult(generic)) { return; } else { appResponse.setValue(PojoUtils.generalize(appResponse.getValue())); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java index 20ef2cea2b9..aa55ebd4317 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericImplFilter.java @@ -107,7 +107,7 @@ else if (isMakingGenericCall(generic, invocation)) { if (ProtocolUtils.isJavaGenericSerialization(generic)) { for (Object arg : args) { - if (!(byte[].class == arg.getClass())) { + if (byte[].class != arg.getClass()) { error(generic, byte[].class.getName(), arg.getClass().getName()); } } @@ -190,9 +190,7 @@ public void onResponse(Result appResponse, Invoker invoker, Invocation invoca if (targetException != null) { try { Field field = Throwable.class.getDeclaredField("detailMessage"); - if (!field.isAccessible()) { - field.setAccessible(true); - } + ReflectUtils.makeAccessible(field); field.set(targetException, exception.getExceptionMessage()); } catch (Throwable e) { logger.warn(e.getMessage(), e); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java index 811a8e5ab86..22e02a0e2d8 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/TokenFilter.java @@ -48,7 +48,9 @@ public Result invoke(Invoker invoker, Invocation inv) Map attachments = inv.getObjectAttachments(); String remoteToken = (attachments == null ? null : (String) attachments.get(TOKEN_KEY)); if (!token.equals(remoteToken)) { - throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider " + RpcContext.getContext().getLocalHost()); + throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider " + RpcContext.getContext().getLocalHost() + + ", consumer incorrect token is " + remoteToken); } } return invoker.invoke(inv); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java index 9fb3fab7d91..348b9ccef50 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/StatItem.java @@ -49,7 +49,7 @@ public boolean isAllowable() { lastResetTime = now; } - if (token.sum() < 0) { + if (token.sum() <= 0) { return false; } token.decrement(); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java index 41a47806c9a..99c9ebd2ff2 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractExporter.java @@ -51,12 +51,19 @@ public Invoker getInvoker() { } @Override - public void unexport() { + final public void unexport() { if (unexported) { return; } unexported = true; getInvoker().destroy(); + afterUnExport(); + } + + /** + * subclasses need to override this method to destroy resources. + */ + public void afterUnExport() { } @Override diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java index 0865f08349f..2c749b45185 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java @@ -26,6 +26,7 @@ import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.remoting.transport.CodecSupport; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.InvokeMode; @@ -44,6 +45,10 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; +import static org.apache.dubbo.remoting.Constants.DEFAULT_REMOTING_SERIALIZATION; +import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY; + /** * This Invoker works on Consumer side. */ @@ -158,6 +163,11 @@ public Result invoke(Invocation inv) throws RpcException { invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation)); RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); + Byte serializationId = CodecSupport.getIDByName(getUrl().getParameter(SERIALIZATION_KEY, DEFAULT_REMOTING_SERIALIZATION)); + if (serializationId != null) { + invocation.put(SERIALIZATION_ID_KEY, serializationId); + } + AsyncRpcResult asyncResult; try { asyncResult = (AsyncRpcResult) doInvoke(invocation); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java index 59491efe444..6be853656fb 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java @@ -1,275 +1,279 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.dubbo.rpc.protocol; - -import org.apache.dubbo.common.Parameters; -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.utils.NetUtils; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.remoting.Channel; -import org.apache.dubbo.remoting.ChannelHandler; -import org.apache.dubbo.remoting.Constants; -import org.apache.dubbo.remoting.RemotingException; -import org.apache.dubbo.remoting.RemotingServer; -import org.apache.dubbo.rpc.Exporter; -import org.apache.dubbo.rpc.Invocation; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.ProtocolServer; -import org.apache.dubbo.rpc.ProxyFactory; -import org.apache.dubbo.rpc.Result; -import org.apache.dubbo.rpc.RpcException; - -import java.net.InetSocketAddress; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CopyOnWriteArrayList; - -import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; - -/** - * AbstractProxyProtocol - */ -public abstract class AbstractProxyProtocol extends AbstractProtocol { - - private final List> rpcExceptions = new CopyOnWriteArrayList>(); - - protected ProxyFactory proxyFactory; - - public AbstractProxyProtocol() { - } - - public AbstractProxyProtocol(Class... exceptions) { - for (Class exception : exceptions) { - addRpcException(exception); - } - } - - public void addRpcException(Class exception) { - this.rpcExceptions.add(exception); - } - - public ProxyFactory getProxyFactory() { - return proxyFactory; - } - - public void setProxyFactory(ProxyFactory proxyFactory) { - this.proxyFactory = proxyFactory; - } - - @Override - @SuppressWarnings("unchecked") - public Exporter export(final Invoker invoker) throws RpcException { - final String uri = serviceKey(invoker.getUrl()); - Exporter exporter = (Exporter) getExport(uri); - if (exporter != null) { - // When modifying the configuration through override, you need to re-expose the newly modified service. - if (Objects.equals(exporter.getInvoker().getUrl(), invoker.getUrl())) { - return exporter; - } - } - final Runnable runnable = doExport(proxyFactory.getProxy(invoker, true), invoker.getInterface(), invoker.getUrl()); - exporter = new AbstractExporter(invoker) { - @Override - public void unexport() { - super.unexport(); - removeExportMap(uri, this); - if (runnable != null) { - try { - runnable.run(); - } catch (Throwable t) { - logger.warn(t.getMessage(), t); - } - } - } - }; - addExportMap(uri, exporter); - return exporter; - } - - @Override - protected Invoker protocolBindingRefer(final Class type, final URL url) throws RpcException { - final Invoker target = proxyFactory.getInvoker(doRefer(type, url), type, url); - Invoker invoker = new AbstractInvoker(type, url) { - @Override - protected Result doInvoke(Invocation invocation) throws Throwable { - try { - Result result = target.invoke(invocation); - // FIXME result is an AsyncRpcResult instance. - Throwable e = result.getException(); - if (e != null) { - for (Class rpcException : rpcExceptions) { - if (rpcException.isAssignableFrom(e.getClass())) { - throw getRpcException(type, url, invocation, e); - } - } - } - return result; - } catch (RpcException e) { - if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) { - e.setCode(getErrorCode(e.getCause())); - } - throw e; - } catch (Throwable e) { - throw getRpcException(type, url, invocation, e); - } - } - }; - invokers.add(invoker); - return invoker; - } - - protected RpcException getRpcException(Class type, URL url, Invocation invocation, Throwable e) { - RpcException re = new RpcException("Failed to invoke remote service: " + type + ", method: " - + invocation.getMethodName() + ", cause: " + e.getMessage(), e); - re.setCode(getErrorCode(e)); - return re; - } - - protected String getAddr(URL url) { - String bindIp = url.getParameter(Constants.BIND_IP_KEY, url.getHost()); - if (url.getParameter(ANYHOST_KEY, false)) { - bindIp = ANYHOST_VALUE; - } - return NetUtils.getIpByHost(bindIp) + ":" + url.getParameter(Constants.BIND_PORT_KEY, url.getPort()); - } - - protected int getErrorCode(Throwable e) { - return RpcException.UNKNOWN_EXCEPTION; - } - - protected abstract Runnable doExport(T impl, Class type, URL url) throws RpcException; - - protected abstract T doRefer(Class type, URL url) throws RpcException; - - protected class ProxyProtocolServer implements ProtocolServer { - - private RemotingServer server; - private String address; - - public ProxyProtocolServer(RemotingServer server) { - this.server = server; - } - - @Override - public RemotingServer getRemotingServer() { - return server; - } - - @Override - public String getAddress() { - return StringUtils.isNotEmpty(address) ? address : server.getUrl().getAddress(); - } - - @Override - public void setAddress(String address) { - this.address = address; - } - - @Override - public URL getUrl() { - return server.getUrl(); - } - - @Override - public void close() { - server.close(); - } - } - - protected abstract class RemotingServerAdapter implements RemotingServer { - - public abstract Object getDelegateServer(); - - /** - * @return - */ - @Override - public boolean isBound() { - return false; - } - - @Override - public Collection getChannels() { - return null; - } - - @Override - public Channel getChannel(InetSocketAddress remoteAddress) { - return null; - } - - @Override - public void reset(Parameters parameters) { - - } - - @Override - public void reset(URL url) { - - } - - @Override - public URL getUrl() { - return null; - } - - @Override - public ChannelHandler getChannelHandler() { - return null; - } - - @Override - public InetSocketAddress getLocalAddress() { - return null; - } - - @Override - public void send(Object message) throws RemotingException { - - } - - @Override - public void send(Object message, boolean sent) throws RemotingException { - - } - - @Override - public void close() { - - } - - @Override - public void close(int timeout) { - - } - - @Override - public void startClose() { - - } - - @Override - public boolean isClosed() { - return false; - } - } - - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dubbo.rpc.protocol; + +import org.apache.dubbo.common.Parameters; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.ChannelHandler; +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.RemotingServer; +import org.apache.dubbo.rpc.Exporter; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.ProtocolServer; +import org.apache.dubbo.rpc.ProxyFactory; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; + +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; + +/** + * AbstractProxyProtocol + */ +public abstract class AbstractProxyProtocol extends AbstractProtocol { + + private final List> rpcExceptions = new CopyOnWriteArrayList>(); + + protected ProxyFactory proxyFactory; + + public AbstractProxyProtocol() { + } + + public AbstractProxyProtocol(Class... exceptions) { + for (Class exception : exceptions) { + addRpcException(exception); + } + } + + public void addRpcException(Class exception) { + this.rpcExceptions.add(exception); + } + + public ProxyFactory getProxyFactory() { + return proxyFactory; + } + + public void setProxyFactory(ProxyFactory proxyFactory) { + this.proxyFactory = proxyFactory; + } + + @Override + @SuppressWarnings("unchecked") + public Exporter export(final Invoker invoker) throws RpcException { + final String uri = serviceKey(invoker.getUrl()); + Exporter exporter = (Exporter) getExport(uri); + if (exporter != null) { + // When modifying the configuration through override, you need to re-expose the newly modified service. + if (Objects.equals(exporter.getInvoker().getUrl(), invoker.getUrl())) { + return exporter; + } + } + final Runnable runnable = doExport(proxyFactory.getProxy(invoker, true), invoker.getInterface(), invoker.getUrl()); + exporter = new AbstractExporter(invoker) { + @Override + public void afterUnExport() { + removeExportMap(uri, this); + if (runnable != null) { + try { + runnable.run(); + } catch (Throwable t) { + logger.warn(t.getMessage(), t); + } + } + } + }; + addExportMap(uri, exporter); + return exporter; + } + + @Override + protected Invoker protocolBindingRefer(final Class type, final URL url) throws RpcException { + final Invoker target = proxyFactory.getInvoker(doRefer(type, url), type, url); + Invoker invoker = new AbstractInvoker(type, url) { + @Override + protected Result doInvoke(Invocation invocation) throws Throwable { + try { + Result result = target.invoke(invocation); + // FIXME result is an AsyncRpcResult instance. + Throwable e = result.getException(); + if (e != null) { + for (Class rpcException : rpcExceptions) { + if (rpcException.isAssignableFrom(e.getClass())) { + throw getRpcException(type, url, invocation, e); + } + } + } + return result; + } catch (RpcException e) { + if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) { + e.setCode(getErrorCode(e.getCause())); + } + throw e; + } catch (Throwable e) { + throw getRpcException(type, url, invocation, e); + } + } + + @Override + public void destroy() { + super.destroy(); + target.destroy(); + invokers.remove(this); + } + }; + invokers.add(invoker); + return invoker; + } + + protected RpcException getRpcException(Class type, URL url, Invocation invocation, Throwable e) { + RpcException re = new RpcException("Failed to invoke remote service: " + type + ", method: " + + invocation.getMethodName() + ", cause: " + e.getMessage(), e); + re.setCode(getErrorCode(e)); + return re; + } + + protected String getAddr(URL url) { + String bindIp = url.getParameter(Constants.BIND_IP_KEY, url.getHost()); + if (url.getParameter(ANYHOST_KEY, false)) { + bindIp = ANYHOST_VALUE; + } + return NetUtils.getIpByHost(bindIp) + ":" + url.getParameter(Constants.BIND_PORT_KEY, url.getPort()); + } + + protected int getErrorCode(Throwable e) { + return RpcException.UNKNOWN_EXCEPTION; + } + + protected abstract Runnable doExport(T impl, Class type, URL url) throws RpcException; + + protected abstract T doRefer(Class type, URL url) throws RpcException; + + protected class ProxyProtocolServer implements ProtocolServer { + + private RemotingServer server; + private String address; + + public ProxyProtocolServer(RemotingServer server) { + this.server = server; + } + + @Override + public RemotingServer getRemotingServer() { + return server; + } + + @Override + public String getAddress() { + return StringUtils.isNotEmpty(address) ? address : server.getUrl().getAddress(); + } + + @Override + public void setAddress(String address) { + this.address = address; + } + + @Override + public URL getUrl() { + return server.getUrl(); + } + + @Override + public void close() { + server.close(); + } + } + + protected abstract class RemotingServerAdapter implements RemotingServer { + + public abstract Object getDelegateServer(); + + /** + * @return + */ + @Override + public boolean isBound() { + return false; + } + + @Override + public Collection getChannels() { + return null; + } + + @Override + public Channel getChannel(InetSocketAddress remoteAddress) { + return null; + } + + @Override + public void reset(Parameters parameters) { + + } + + @Override + public void reset(URL url) { + + } + + @Override + public URL getUrl() { + return null; + } + + @Override + public ChannelHandler getChannelHandler() { + return null; + } + + @Override + public InetSocketAddress getLocalAddress() { + return null; + } + + @Override + public void send(Object message) throws RemotingException { + + } + + @Override + public void send(Object message, boolean sent) throws RemotingException { + + } + + @Override + public void close() { + + } + + @Override + public void close(int timeout) { + + } + + @Override + public void startClose() { + + } + + @Override + public boolean isClosed() { + return false; + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/FilterNode.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/FilterNode.java new file mode 100644 index 00000000000..00da9130f9c --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/FilterNode.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.dubbo.rpc.protocol; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Filter; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.ListenableFilter; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; + +/** + * @see org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper + * + */ +class FilterNode implements Invoker{ + private final Invoker invoker; + private final Invoker next; + private final Filter filter; + + public FilterNode(final Invoker invoker, final Invoker next, final Filter filter) { + this.invoker = invoker; + this.next = next; + this.filter = filter; + } + + @Override + public Class getInterface() { + return invoker.getInterface(); + } + + @Override + public URL getUrl() { + return invoker.getUrl(); + } + + @Override + public boolean isAvailable() { + return invoker.isAvailable(); + } + + @Override + public Result invoke(Invocation invocation) throws RpcException { + Result asyncResult; + try { + asyncResult = filter.invoke(next, invocation); + } catch (Exception e) { + if (filter instanceof ListenableFilter) { + ListenableFilter listenableFilter = ((ListenableFilter) filter); + try { + Filter.Listener listener = listenableFilter.listener(invocation); + if (listener != null) { + listener.onError(e, invoker, invocation); + } + } finally { + listenableFilter.removeListener(invocation); + } + } else if (filter instanceof Filter.Listener) { + Filter.Listener listener = (Filter.Listener) filter; + listener.onError(e, invoker, invocation); + } + throw e; + } finally { + + } + return asyncResult.whenCompleteWithContext((r, t) -> { + if (filter instanceof ListenableFilter) { + ListenableFilter listenableFilter = ((ListenableFilter) filter); + Filter.Listener listener = listenableFilter.listener(invocation); + try { + if (listener != null) { + if (t == null) { + listener.onResponse(r, invoker, invocation); + } else { + listener.onError(t, invoker, invocation); + } + } + } finally { + listenableFilter.removeListener(invocation); + } + } else if (filter instanceof Filter.Listener) { + Filter.Listener listener = (Filter.Listener) filter; + if (t == null) { + listener.onResponse(r, invoker, invocation); + } else { + listener.onError(t, invoker, invocation); + } + } + }); + } + + @Override + public void destroy() { + invoker.destroy(); + } + + @Override + public String toString() { + return invoker.toString(); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java index 74ac50cad44..826abf5ef1b 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolFilterWrapper.java @@ -23,12 +23,9 @@ import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Filter; -import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.ListenableFilter; import org.apache.dubbo.rpc.Protocol; import org.apache.dubbo.rpc.ProtocolServer; -import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; import java.util.List; @@ -58,84 +55,7 @@ private static Invoker buildInvokerChain(final Invoker invoker, String if (!filters.isEmpty()) { for (int i = filters.size() - 1; i >= 0; i--) { final Filter filter = filters.get(i); - final Invoker next = last; - last = new Invoker() { - - @Override - public Class getInterface() { - return invoker.getInterface(); - } - - @Override - public URL getUrl() { - return invoker.getUrl(); - } - - @Override - public boolean isAvailable() { - return invoker.isAvailable(); - } - - @Override - public Result invoke(Invocation invocation) throws RpcException { - Result asyncResult; - try { - asyncResult = filter.invoke(next, invocation); - } catch (Exception e) { - if (filter instanceof ListenableFilter) { - ListenableFilter listenableFilter = ((ListenableFilter) filter); - try { - Filter.Listener listener = listenableFilter.listener(invocation); - if (listener != null) { - listener.onError(e, invoker, invocation); - } - } finally { - listenableFilter.removeListener(invocation); - } - } else if (filter instanceof Filter.Listener) { - Filter.Listener listener = (Filter.Listener) filter; - listener.onError(e, invoker, invocation); - } - throw e; - } finally { - - } - return asyncResult.whenCompleteWithContext((r, t) -> { - if (filter instanceof ListenableFilter) { - ListenableFilter listenableFilter = ((ListenableFilter) filter); - Filter.Listener listener = listenableFilter.listener(invocation); - try { - if (listener != null) { - if (t == null) { - listener.onResponse(r, invoker, invocation); - } else { - listener.onError(t, invoker, invocation); - } - } - } finally { - listenableFilter.removeListener(invocation); - } - } else if (filter instanceof Filter.Listener) { - Filter.Listener listener = (Filter.Listener) filter; - if (t == null) { - listener.onResponse(r, invoker, invocation); - } else { - listener.onError(t, invoker, invocation); - } - } - }); - } - - @Override - public void destroy() { - invoker.destroy(); - } - - @Override - public String toString() { - return invoker.toString(); - } - }; + last = new FilterNode(invoker, last, filter); } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java index 837750f91a4..39b09928c95 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java @@ -22,10 +22,9 @@ import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.service.Destroyable; +import org.apache.dubbo.rpc.service.EchoService; import org.apache.dubbo.rpc.service.GenericService; -import com.alibaba.dubbo.rpc.service.EchoService; - import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -60,7 +59,7 @@ public T getProxy(Invoker invoker, boolean generic) throws RpcException { } if (generic) { - if (!GenericService.class.isAssignableFrom(invoker.getInterface())) { + if (GenericService.class.equals(invoker.getInterface()) || !GenericService.class.isAssignableFrom(invoker.getInterface())) { interfaces.add(com.alibaba.dubbo.rpc.service.GenericService.class); } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java index 1b1d592d97d..79168c9a1b2 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java @@ -82,9 +82,9 @@ public void destroy() { public Result invoke(Invocation invocation) throws RpcException { try { Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()); - CompletableFuture future = wrapWithFuture(value); + CompletableFuture future = wrapWithFuture(value); CompletableFuture appResponseFuture = future.handle((obj, t) -> { - AppResponse result = new AppResponse(); + AppResponse result = new AppResponse(invocation); if (t != null) { if (t instanceof CompletionException) { result.setException(t.getCause()); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java index 69f1d06944b..e6cfafe0c20 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java @@ -19,6 +19,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.Constants; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; @@ -26,6 +27,7 @@ import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ConsumerModel; +import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -39,6 +41,17 @@ public class InvokerInvocationHandler implements InvocationHandler { private URL url; private String protocolServiceKey; + public static Field stackTraceField; + + static { + try { + stackTraceField = Throwable.class.getDeclaredField("stackTrace"); + ReflectUtils.makeAccessible(stackTraceField); + } catch (NoSuchFieldException e) { + // ignore + } + } + public InvokerInvocationHandler(Invoker handler) { this.invoker = handler; this.url = invoker.getUrl(); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java index e9ff35896e1..ca2eac2778c 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java @@ -17,10 +17,15 @@ package org.apache.dubbo.rpc.support; import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcContext; import com.alibaba.fastjson.JSON; +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; + import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -262,5 +267,15 @@ private Object get(String key) { private void set(String key, Object value) { data.put(key, value); } + + public void buildAccessLogData(Invoker invoker, Invocation inv) { + setServiceName(invoker.getInterface().getName()); + setMethodName(inv.getMethodName()); + setVersion(invoker.getUrl().getParameter(VERSION_KEY)); + setGroup(invoker.getUrl().getParameter(GROUP_KEY)); + setInvocationTime(new Date()); + setTypes(inv.getParameterTypes()); + setArguments(inv.getArguments()); + } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java index 3e8f81effc7..5e9e74a9a6b 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java @@ -43,6 +43,7 @@ import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; import static org.apache.dubbo.rpc.Constants.ID_KEY; import static org.apache.dubbo.rpc.Constants.RETURN_KEY; + /** * RpcUtils */ @@ -100,7 +101,7 @@ public static Long getInvocationId(Invocation inv) { */ public static void attachInvocationIdIfAsync(URL url, Invocation inv) { if (isAttachInvocationId(url, inv) && getInvocationId(inv) == null && inv instanceof RpcInvocation) { - ((RpcInvocation) inv).setAttachment(ID_KEY, String.valueOf(INVOKE_ID.getAndIncrement())); + inv.setAttachment(ID_KEY, String.valueOf(INVOKE_ID.getAndIncrement())); } } @@ -156,6 +157,14 @@ public static Class[] getParameterTypes(Invocation invocation) { public static boolean isAsync(URL url, Invocation inv) { boolean isAsync; + + if (inv instanceof RpcInvocation) { + RpcInvocation rpcInvocation = (RpcInvocation) inv; + if (rpcInvocation.getInvokeMode() != null) { + return rpcInvocation.getInvokeMode() == InvokeMode.ASYNC; + } + } + if (Boolean.TRUE.toString().equals(inv.getAttachment(ASYNC_KEY))) { isAsync = true; } else { @@ -189,6 +198,13 @@ public static boolean isEcho(String parameterTypesDesc, String method) { } public static InvokeMode getInvokeMode(URL url, Invocation inv) { + if (inv instanceof RpcInvocation) { + RpcInvocation rpcInvocation = (RpcInvocation) inv; + if (rpcInvocation.getInvokeMode() != null) { + return rpcInvocation.getInvokeMode(); + } + } + if (isReturnTypeFuture(inv)) { return InvokeMode.FUTURE; } else if (isAsync(url, inv)) { diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java index 380b2c3c40e..3c7b495b954 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java @@ -139,6 +139,7 @@ public void testObject() { map.keySet().forEach(context::remove); Assertions.assertNull(context.get("_11")); + RpcContext.removeContext(); } @Test diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java index 5bfcb4b0dd1..97184cd3ce7 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/AccessLogFilterTest.java @@ -17,7 +17,9 @@ package org.apache.dubbo.rpc.filter; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.DubboAppender; import org.apache.dubbo.common.utils.LogUtil; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; @@ -51,6 +53,7 @@ public void testInvokeException() { accessLogFilter.invoke(invoker, invocation); assertEquals(1, LogUtil.findMessage("Exception in AccessLogFilter of service")); LogUtil.stop(); + DubboAppender.clear(); } // TODO how to assert thread action @@ -62,7 +65,7 @@ public void testDefault() throws NoSuchFieldException, IllegalAccessException { Invocation invocation = new MockInvocation(); Field field = AccessLogFilter.class.getDeclaredField("LOG_ENTRIES"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); assertTrue(((Map) field.get(AccessLogFilter.class)).isEmpty()); accessLogFilter.invoke(invoker, invocation); @@ -82,4 +85,4 @@ public void testCustom() { accessLogFilter.invoke(invoker, invocation); } -} \ No newline at end of file +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java index c49aa9278fe..48ae21712a5 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.filter; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.AsyncRpcResult; import org.apache.dubbo.rpc.Invocation; @@ -75,6 +76,8 @@ public void testInvokeWithDefault() throws Exception { @Test public void testInvokeWithJavaException() throws Exception { + // temporary enable native java generic serialize + System.setProperty(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, "true"); Assertions.assertThrows(RpcException.class, () -> { Method genericInvoke = GenericService.class.getMethods()[0]; @@ -95,6 +98,7 @@ public void testInvokeWithJavaException() throws Exception { genericFilter.invoke(invoker, invocation); }); + System.clearProperty(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE); } @Test diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java index 6633f587a9c..53969affc40 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/DefaultTPSLimiterTest.java @@ -29,6 +29,7 @@ public class DefaultTPSLimiterTest { + private static final int TEST_LIMIT_RATE = 2; private DefaultTPSLimiter defaultTPSLimiter = new DefaultTPSLimiter(); @Test @@ -36,9 +37,9 @@ public void testIsAllowable() throws Exception { Invocation invocation = new MockInvocation(); URL url = URL.valueOf("test://test"); url = url.addParameter(INTERFACE_KEY, "org.apache.dubbo.rpc.file.TpsService"); - url = url.addParameter(TPS_LIMIT_RATE_KEY, 2); + url = url.addParameter(TPS_LIMIT_RATE_KEY, TEST_LIMIT_RATE); url = url.addParameter(TPS_LIMIT_INTERVAL_KEY, 1000); - for (int i = 0; i < 3; i++) { + for (int i = 1; i <= TEST_LIMIT_RATE; i++) { Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation)); } } @@ -48,10 +49,10 @@ public void testIsNotAllowable() throws Exception { Invocation invocation = new MockInvocation(); URL url = URL.valueOf("test://test"); url = url.addParameter(INTERFACE_KEY, "org.apache.dubbo.rpc.file.TpsService"); - url = url.addParameter(TPS_LIMIT_RATE_KEY, 2); + url = url.addParameter(TPS_LIMIT_RATE_KEY, TEST_LIMIT_RATE); url = url.addParameter(TPS_LIMIT_INTERVAL_KEY, 1000); - for (int i = 0; i < 4; i++) { - if (i == 3) { + for (int i = 1; i <= TEST_LIMIT_RATE + 1; i++) { + if (i == TEST_LIMIT_RATE + 1) { Assertions.assertFalse(defaultTPSLimiter.isAllowable(url, invocation)); } else { Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation)); @@ -65,14 +66,17 @@ public void testConfigChange() throws Exception { Invocation invocation = new MockInvocation(); URL url = URL.valueOf("test://test"); url = url.addParameter(INTERFACE_KEY, "org.apache.dubbo.rpc.file.TpsService"); - url = url.addParameter(TPS_LIMIT_RATE_KEY, 2); + url = url.addParameter(TPS_LIMIT_RATE_KEY, TEST_LIMIT_RATE); url = url.addParameter(TPS_LIMIT_INTERVAL_KEY, 1000); - for (int i = 0; i < 3; i++) { + for (int i = 1; i <= TEST_LIMIT_RATE; i++) { Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation)); } - url = url.addParameter(TPS_LIMIT_RATE_KEY, 2000); - for (int i = 0; i < 3; i++) { + final int tenTimesLimitRate = TEST_LIMIT_RATE * 10; + url = url.addParameter(TPS_LIMIT_RATE_KEY, tenTimesLimitRate); + for (int i = 1; i <= tenTimesLimitRate; i++) { Assertions.assertTrue(defaultTPSLimiter.isAllowable(url, invocation)); } + + Assertions.assertFalse(defaultTPSLimiter.isAllowable(url, invocation)); } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java index 78c0c9cb0ec..e8c6bf80233 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/tps/StatItemTest.java @@ -42,4 +42,15 @@ public void testIsAllowable() throws Exception { assertEquals(4, statItem.getToken()); } + @Test + public void testAccuracy() throws Exception { + final int EXPECTED_RATE = 5; + statItem = new StatItem("test", EXPECTED_RATE, 60_000L); + for (int i = 1; i <= EXPECTED_RATE; i++) { + assertEquals(true, statItem.isAllowable()); + } + + // Must block the 6th item + assertEquals(false, statItem.isAllowable()); + } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java index 5085f5891da..3b870682aa4 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java @@ -296,8 +296,8 @@ public static Object decodeInvocationArgument(Channel channel, RpcInvocation inv } return inObject; } - byte callBackStatus = isCallBack(url, inv.getProtocolServiceKey(), inv.getMethodName(), paraIndex); - switch (callBackStatus) { + byte callbackstatus = isCallBack(url, inv.getProtocolServiceKey(), inv.getMethodName(), paraIndex); + switch (callbackstatus) { case CallbackServiceCodec.CALLBACK_CREATE: try { return referOrDestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), true); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java index f286ddb5380..ebfc41c0ab8 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.protocol.dubbo; +import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Cleanable; @@ -47,6 +48,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY; import static org.apache.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument; public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable { @@ -95,10 +98,15 @@ public void encode(Channel channel, OutputStream output, Object message) throws throw new UnsupportedOperationException(); } + private void checkSerializationTypeFromRemote() { + + } + @Override public Object decode(Channel channel, InputStream input) throws IOException { ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType) .deserialize(channel.getUrl(), input); + this.put(SERIALIZATION_ID_KEY, serializationType); String dubboVersion = in.readUTF(); request.setVersion(dubboVersion); @@ -106,7 +114,8 @@ public Object decode(Channel channel, InputStream input) throws IOException { String path = in.readUTF(); setAttachment(PATH_KEY, path); - setAttachment(VERSION_KEY, in.readUTF()); + String version = in.readUTF(); + setAttachment(VERSION_KEY, version); setMethodName(in.readUTF()); @@ -114,6 +123,9 @@ public Object decode(Channel channel, InputStream input) throws IOException { setParameterTypesDesc(desc); try { + if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, false)) { + CodecSupport.checkSerialization(path, version, serializationType); + } Object[] args = DubboCodec.EMPTY_OBJECT_ARRAY; Class[] pts = DubboCodec.EMPTY_CLASS_ARRAY; if (desc.length() > 0) { diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java index 83db98600be..30ee3a0e3e1 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.rpc.protocol.dubbo; +import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Cleanable; @@ -38,6 +39,9 @@ import java.io.OutputStream; import java.lang.reflect.Type; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY; + public class DecodeableRpcResult extends AppResponse implements Codec, Decodeable { private static final Logger log = LoggerFactory.getLogger(DecodeableRpcResult.class); @@ -114,6 +118,14 @@ public Object decode(Channel channel, InputStream input) throws IOException { public void decode() throws Exception { if (!hasDecoded && channel != null && inputStream != null) { try { + if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, false)) { + Object serializationType_obj = invocation.get(SERIALIZATION_ID_KEY); + if (serializationType_obj != null) { + if ((byte) serializationType_obj != serializationType) { + throw new IOException("Unexpected serialization id:" + serializationType + " received from network, please check if the peer send the right id."); + } + } + } decode(channel, inputStream); } catch (Throwable e) { if (log.isWarnEnabled()) { @@ -160,7 +172,7 @@ private void handleException(ObjectInput in) throws IOException { private void handleAttachment(ObjectInput in) throws IOException { try { - setObjectAttachments(in.readAttachments()); + addObjectAttachments(in.readAttachments()); } catch (ClassNotFoundException e) { rethrow(e); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java index 889f2cebad7..c6c7994de36 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java @@ -23,16 +23,19 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.codec.ExchangeCodec; import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcInvocation; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -79,8 +82,14 @@ protected Object decodeBody(Channel channel, InputStream is, byte[] header) thro if (status == Response.OK) { Object data; if (res.isEvent()) { - ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto); - data = decodeEventData(channel, in); + byte[] eventPayload = CodecSupport.getPayload(is); + if (CodecSupport.isHeartBeat(eventPayload, proto)) { + // heart beat response data is always null; + data = null; + } else { + ObjectInput in = CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto); + data = decodeEventData(channel, in, eventPayload); + } } else { DecodeableRpcResult result; if (channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD)) { @@ -118,8 +127,14 @@ protected Object decodeBody(Channel channel, InputStream is, byte[] header) thro try { Object data; if (req.isEvent()) { - ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto); - data = decodeEventData(channel, in); + byte[] eventPayload = CodecSupport.getPayload(is); + if (CodecSupport.isHeartBeat(eventPayload, proto)) { + // heart beat response data is always null; + data = null; + } else { + ObjectInput in = CodecSupport.deserialize(channel.getUrl(), new ByteArrayInputStream(eventPayload), proto); + data = decodeEventData(channel, in, eventPayload); + } } else { DecodeableRpcInvocation inv; if (channel.getUrl().getParameter(DECODE_IN_IO_THREAD_KEY, DEFAULT_DECODE_IN_IO_THREAD)) { @@ -213,4 +228,21 @@ protected void encodeResponseData(Channel channel, ObjectOutput out, Object data out.writeAttachments(result.getObjectAttachments()); } } + + @Override + protected Serialization getSerialization(Channel channel, Request req) { + if (!(req.getData() instanceof Invocation)) { + return super.getSerialization(channel, req); + } + return DubboCodecSupport.getRequestSerialization(channel.getUrl(), (Invocation) req.getData()); + } + + @Override + protected Serialization getSerialization(Channel channel, Response res) { + if (!(res.getResult() instanceof AppResponse)) { + return super.getSerialization(channel, res); + } + return DubboCodecSupport.getResponseSerialization(channel.getUrl(), (AppResponse) res.getResult()); + } + } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java new file mode 100644 index 00000000000..2dbb312b431 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.dubbo; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.remoting.Constants; +import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.Invocation; + +import static org.apache.dubbo.rpc.Constants.INVOCATION_KEY; +import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY; + +public class DubboCodecSupport { + + public static Serialization getRequestSerialization(URL url, Invocation invocation) { + Object serializationType_obj = invocation.get(SERIALIZATION_ID_KEY); + if (serializationType_obj != null) { + return CodecSupport.getSerializationById((byte) serializationType_obj); + } + return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension( + url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION)); + } + + public static Serialization getResponseSerialization(URL url, AppResponse appResponse) { + Object invocation_obj = appResponse.getAttribute(INVOCATION_KEY); + if (invocation_obj != null) { + Invocation invocation = (Invocation) invocation_obj; + Object serializationType_obj = invocation.get(SERIALIZATION_ID_KEY); + if (serializationType_obj != null) { + return CodecSupport.getSerializationById((byte) serializationType_obj); + } + } + return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension( + url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION)); + } +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java index c4c3285050d..3d64dd2b952 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboExporter.java @@ -36,9 +36,7 @@ public DubboExporter(Invoker invoker, String key, DelegateExporterMap delegat } @Override - public void unexport() { - super.unexport(); + public void afterUnExport() { delegateExporterMap.removeExportMap(key, this); } - } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java index c415ac49c6a..c4c94747563 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvoker.java @@ -43,11 +43,13 @@ import java.util.concurrent.locks.ReentrantLock; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_VERSION; import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_TIMEOUT_COUNTDOWN_KEY; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_ATTACHMENT_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIME_COUNTDOWN_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; @@ -75,7 +77,7 @@ public DubboInvoker(Class serviceType, URL url, ExchangeClient[] clients, Set super(serviceType, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY}); this.clients = clients; // get version. - this.version = url.getParameter(VERSION_KEY, "0.0.0"); + this.version = url.getParameter(VERSION_KEY, DEFAULT_VERSION); this.invokers = invokers; } @@ -95,6 +97,7 @@ protected Result doInvoke(final Invocation invocation) throws Throwable { try { boolean isOneway = RpcUtils.isOneway(getUrl(), invocation); int timeout = calculateTimeout(invocation, methodName); + invocation.put(TIMEOUT_KEY, timeout); if (isOneway) { boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false); currentClient.send(inv, isSent); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java index 5ee2d524f6a..314650252db 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java @@ -57,7 +57,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.function.Function; import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; @@ -100,9 +99,10 @@ public class DubboProtocol extends AbstractProtocol { /** * + * {@link Map} */ - private final Map> referenceClientMap = new ConcurrentHashMap<>(); - private final ConcurrentMap locks = new ConcurrentHashMap<>(); + private final Map referenceClientMap = new ConcurrentHashMap<>(); + private static final Object PENDING_OBJECT = new Object(); private final Set optimizers = new ConcurrentHashSet<>(); private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() { @@ -221,6 +221,7 @@ public static DubboProtocol getDubboProtocol() { return INSTANCE; } + @Override public Collection> getExporters() { return Collections.unmodifiableCollection(exporterMap.values()); } @@ -388,11 +389,9 @@ private void optimizeSerialization(URL url) throws RpcException { } catch (ClassNotFoundException e) { throw new RpcException("Cannot find the serialization optimizer class: " + className, e); - } catch (InstantiationException e) { + } catch (InstantiationException | IllegalAccessException e) { throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e); - } catch (IllegalAccessException e) { - throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e); } } @@ -446,53 +445,76 @@ private ExchangeClient[] getClients(URL url) { * @param url * @param connectNum connectNum must be greater than or equal to 1 */ + @SuppressWarnings("unchecked") private List getSharedClient(URL url, int connectNum) { String key = url.getAddress(); - List clients = referenceClientMap.get(key); - if (checkClientCanUse(clients)) { - batchClientRefIncr(clients); - return clients; + Object clients = referenceClientMap.get(key); + if (clients instanceof List) { + List typedClients = (List) clients; + if (checkClientCanUse(typedClients)) { + batchClientRefIncr(typedClients); + return typedClients; + } } - locks.putIfAbsent(key, new Object()); - synchronized (locks.get(key)) { - clients = referenceClientMap.get(key); - // dubbo check - if (checkClientCanUse(clients)) { - batchClientRefIncr(clients); - return clients; + List typedClients = null; + + synchronized (referenceClientMap) { + for (; ; ) { + clients = referenceClientMap.get(key); + + if (clients instanceof List) { + typedClients = (List) clients; + if (checkClientCanUse(typedClients)) { + batchClientRefIncr(typedClients); + return typedClients; + } else { + referenceClientMap.put(key, PENDING_OBJECT); + break; + } + } else if (clients == PENDING_OBJECT) { + try { + referenceClientMap.wait(); + } catch (InterruptedException ignored) { + } + } else { + referenceClientMap.put(key, PENDING_OBJECT); + break; + } } + } + try { // connectNum must be greater than or equal to 1 connectNum = Math.max(connectNum, 1); // If the clients is empty, then the first initialization is - if (CollectionUtils.isEmpty(clients)) { - clients = buildReferenceCountExchangeClientList(url, connectNum); - referenceClientMap.put(key, clients); - + if (CollectionUtils.isEmpty(typedClients)) { + typedClients = buildReferenceCountExchangeClientList(url, connectNum); } else { - for (int i = 0; i < clients.size(); i++) { - ReferenceCountExchangeClient referenceCountExchangeClient = clients.get(i); + for (int i = 0; i < typedClients.size(); i++) { + ReferenceCountExchangeClient referenceCountExchangeClient = typedClients.get(i); // If there is a client in the list that is no longer available, create a new one to replace him. if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) { - clients.set(i, buildReferenceCountExchangeClient(url)); + typedClients.set(i, buildReferenceCountExchangeClient(url)); continue; } - referenceCountExchangeClient.incrementAndGetCount(); } } - - /* - * I understand that the purpose of the remove operation here is to avoid the expired url key - * always occupying this memory space. - */ - locks.remove(key); - - return clients; + } finally { + synchronized (referenceClientMap) { + if (typedClients == null) { + referenceClientMap.remove(key); + } else { + referenceClientMap.put(key, typedClients); + } + referenceClientMap.notifyAll(); + } } + return typedClients; + } /** @@ -508,7 +530,7 @@ private boolean checkClientCanUse(List referenceCo for (ReferenceCountExchangeClient referenceCountExchangeClient : referenceCountExchangeClients) { // As long as one client is not available, you need to replace the unavailable client with the available one. - if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) { + if (referenceCountExchangeClient == null || referenceCountExchangeClient.getCount() <= 0 || referenceCountExchangeClient.isClosed()) { return false; } } @@ -600,6 +622,7 @@ private ExchangeClient initClient(URL url) { } @Override + @SuppressWarnings("unchecked") public void destroy() { for (String key : new ArrayList<>(serverMap.keySet())) { ProtocolServer protocolServer = serverMap.remove(key); @@ -623,14 +646,17 @@ public void destroy() { } for (String key : new ArrayList<>(referenceClientMap.keySet())) { - List clients = referenceClientMap.remove(key); + Object clients = referenceClientMap.remove(key); + if (clients instanceof List) { + List typedClients = (List) clients; - if (CollectionUtils.isEmpty(clients)) { - continue; - } + if (CollectionUtils.isEmpty(typedClients)) { + continue; + } - for (ReferenceCountExchangeClient client : clients) { - closeReferenceCountExchangeClient(client); + for (ReferenceCountExchangeClient client : typedClients) { + closeReferenceCountExchangeClient(client); + } } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java index 8af87c82bd1..2947e7ef04b 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java @@ -35,8 +35,8 @@ import java.util.concurrent.locks.ReentrantLock; import static org.apache.dubbo.remoting.Constants.SEND_RECONNECT_KEY; -import static org.apache.dubbo.rpc.protocol.dubbo.Constants.LAZY_CONNECT_INITIAL_STATE_KEY; import static org.apache.dubbo.rpc.protocol.dubbo.Constants.DEFAULT_LAZY_CONNECT_INITIAL_STATE; +import static org.apache.dubbo.rpc.protocol.dubbo.Constants.LAZY_CONNECT_INITIAL_STATE_KEY; /** * dubbo protocol support class. @@ -135,7 +135,7 @@ public CompletableFuture request(Object request, int timeout, ExecutorSe private void warning() { if (requestWithWarning) { if (warningcount.get() % warning_period == 0) { - logger.warn(new IllegalStateException("safe guard client , should not be called ,must have a bug.")); + logger.warn(url.getAddress() + " " + url.getServiceKey() + " safe guard client , should not be called ,must have a bug."); } warningcount.incrementAndGet(); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java index 8c4d07be707..ef3e00fb5cd 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java @@ -29,7 +29,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; -import static org.apache.dubbo.remoting.Constants.RECONNECT_KEY; import static org.apache.dubbo.remoting.Constants.SEND_RECONNECT_KEY; import static org.apache.dubbo.rpc.protocol.dubbo.Constants.LAZY_CONNECT_INITIAL_STATE_KEY; @@ -181,7 +180,7 @@ public void startClose() { private void replaceWithLazyClient() { // this is a defensive operation to avoid client is closed by accident, the initial state of the client is false URL lazyUrl = url.addParameter(LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.TRUE) - .addParameter(RECONNECT_KEY, Boolean.FALSE) + //.addParameter(RECONNECT_KEY, Boolean.FALSE) .addParameter(SEND_RECONNECT_KEY, Boolean.TRUE.toString()) .addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true); @@ -204,5 +203,9 @@ public boolean isClosed() { public void incrementAndGetCount() { referenceCount.incrementAndGet(); } + + public int getCount() { + return referenceCount.get(); + } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java index ee922afed32..955043e587a 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Invoker; @@ -79,10 +80,8 @@ private void fireInvokeCallback(final Invoker invoker, final Invocation invoc if (onInvokeMethod == null || onInvokeInst == null) { throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a oninvoke callback config , but no such " + (onInvokeMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl()); } - if (!onInvokeMethod.isAccessible()) { - onInvokeMethod.setAccessible(true); - } + ReflectUtils.makeAccessible(onInvokeMethod); Object[] params = invocation.getArguments(); try { onInvokeMethod.invoke(onInvokeInst, params); @@ -110,9 +109,7 @@ private void fireReturnCallback(final Invoker invoker, final Invocation invoc if (onReturnMethod == null || onReturnInst == null) { throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onreturn callback config , but no such " + (onReturnMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl()); } - if (!onReturnMethod.isAccessible()) { - onReturnMethod.setAccessible(true); - } + ReflectUtils.makeAccessible(onReturnMethod); Object[] args = invocation.getArguments(); Object[] params; @@ -155,9 +152,7 @@ private void fireThrowCallback(final Invoker invoker, final Invocation invoca if (onthrowMethod == null || onthrowInst == null) { throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onthrow callback config , but no such " + (onthrowMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl()); } - if (!onthrowMethod.isAccessible()) { - onthrowMethod.setAccessible(true); - } + ReflectUtils.makeAccessible(onthrowMethod); Class[] rParaTypes = onthrowMethod.getParameterTypes(); if (rParaTypes[0].isAssignableFrom(exception.getClass())) { try { diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java index 4bc08005a1b..a99bcc90e4f 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java @@ -109,9 +109,10 @@ public void destroyService() { @Test public void TestCallbackNormalWithBindPort() throws Exception { initOrResetUrl(1, 10000000); - consumerUrl = serviceURL.addParameter(Constants.BIND_PORT_KEY,"7653"); + int port = NetUtils.getAvailablePort(); + consumerUrl = serviceURL.addParameter(Constants.BIND_PORT_KEY, port); initOrResetService(); - + final AtomicInteger count = new AtomicInteger(0); demoProxy.xxx(new IDemoCallback() { @@ -316,12 +317,12 @@ public void xxx(final IDemoCallback callback, String arg1, final int runs, final Thread t = new Thread(new Runnable() { public void run() { for (int i = 0; i < runs; i++) { - String ret = callback.yyy("server invoke callback : arg:" + System.currentTimeMillis()); - System.out.println("callback result is :" + ret); try { + String ret = callback.yyy("server invoke callback : arg:" + System.currentTimeMillis()); + System.out.println("callback result is :" + ret); Thread.sleep(sleep); - } catch (InterruptedException e) { - e.printStackTrace(); + } catch (Exception e) { + // ignore daemon thread error } } } @@ -355,7 +356,6 @@ public void run() { try { callback.yyy("this is callback msg,current time is :" + System.currentTimeMillis()); } catch (Exception e) { - e.printStackTrace(); callbacks.remove(callback); } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java similarity index 97% rename from dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java rename to dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java index d2c318bc427..dd296ccfe86 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboInvokerAvailableTest.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.rpc.Exporter; @@ -42,7 +43,7 @@ /** * Check available status for dubboInvoker */ -public class DubboInvokerAvilableTest { +public class DubboInvokerAvailableTest { private static DubboProtocol protocol; private static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); @@ -90,6 +91,7 @@ public void test_Normal_ChannelReadOnly() throws Exception { } @Disabled + @Test public void test_normal_channel_close_wait_gracefully() throws Exception { int testPort = NetUtils.getAvailablePort(); URL url = URL.valueOf("dubbo://127.0.0.1:" + testPort + "/org.apache.dubbo.rpc.protocol.dubbo.IDemoService?scope=true&lazy=false"); @@ -155,7 +157,7 @@ public void test_Lazy_ChannelReadOnly() throws Exception { private ExchangeClient[] getClients(DubboInvoker invoker) throws Exception { Field field = DubboInvoker.class.getDeclaredField("clients"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); ExchangeClient[] clients = (ExchangeClient[]) field.get(invoker); Assertions.assertEquals(1, clients.length); return clients; diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java index e55f7ac31d4..3377eeb59cf 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.HashMap; @@ -94,7 +95,7 @@ public void testDubboProtocol() throws Exception { service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName() + "?client=netty").addParameter("timeout", 3000L))); // test netty client - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (int i = 0; i < 1024 * 32 + 32; i++) buf.append('A'); System.out.println(service.stringLength(buf.toString())); @@ -108,6 +109,7 @@ public void testDubboProtocol() throws Exception { assertEquals(echo.$echo(1234), 1234); } + @Disabled("Mina has been moved to a separate project") @Test public void testDubboProtocolWithMina() throws Exception { DemoService service = new DemoServiceImpl(); @@ -132,7 +134,7 @@ public void testDubboProtocolWithMina() throws Exception { service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName() + "?client=mina").addParameter("timeout", 3000L))); // test netty client - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (int i = 0; i < 1024 * 32 + 32; i++) buf.append('A'); System.out.println(service.stringLength(buf.toString())); @@ -214,6 +216,7 @@ public void testReturnNonSerialized() throws Exception { service.returnNonSerialized(); Assertions.fail(); } catch (RpcException e) { + e.printStackTrace(); Assertions.assertTrue(e.getMessage().contains("org.apache.dubbo.rpc.protocol.dubbo.support.NonSerialized must implement java.io.Serializable")); } } @@ -228,4 +231,22 @@ public void testRemoteApplicationName() throws Exception { service = proxy.getProxy(protocol.refer(DemoService.class, url)); assertEquals(service.getRemoteApplicationName(), "consumer"); } + + @Test + public void testPayloadOverException() throws Exception { + DemoService service = new DemoServiceImpl(); + int port = NetUtils.getAvailablePort(); + protocol.export(proxy.getInvoker(service, DemoService.class, + URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName()).addParameter("payload", 10 * 1024))); + service = proxy.getProxy(protocol.refer(DemoService.class, + URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName()).addParameter("timeout", + 6000L).addParameter("payload", 160))); + try { + service.download(300); + Assertions.fail(); + } catch (Exception expected) { + Assertions.assertTrue(expected.getMessage().contains("Data length too large")); + } + + } } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java index ce820681371..b5e8066c2fa 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClientTest.java @@ -21,6 +21,7 @@ import org.apache.dubbo.common.utils.DubboAppender; import org.apache.dubbo.common.utils.LogUtil; import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.remoting.exchange.ExchangeClient; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invoker; @@ -244,7 +245,7 @@ private ExchangeClient getClient(Invoker invoker) { ReferenceCountExchangeClient client = getReferenceClient(invoker); try { Field clientField = ReferenceCountExchangeClient.class.getDeclaredField("client"); - clientField.setAccessible(true); + ReflectUtils.makeAccessible(clientField); return (ExchangeClient) clientField.get(client); } catch (Exception e) { e.printStackTrace(); @@ -278,7 +279,7 @@ private List getInvokerClientList(Invoker invoker) { @SuppressWarnings("rawtypes") DubboInvoker dInvoker = (DubboInvoker) ((AsyncToSyncInvoker) invoker).getInvoker(); try { Field clientField = DubboInvoker.class.getDeclaredField("clients"); - clientField.setAccessible(true); + ReflectUtils.makeAccessible(clientField); ExchangeClient[] clients = (ExchangeClient[]) clientField.get(dInvoker); List clientList = new ArrayList(clients.length); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java index 3c66d7558ae..c0ffd8a1d21 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/decode/DubboTelnetDecodeTest.java @@ -42,6 +42,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -261,7 +262,8 @@ public CompletableFuture reply(ExchangeChannel channel, Object msg) { * * @throws InterruptedException */ - // @Test + @Disabled + @Test public void testTelnetTelnetDecoded() throws InterruptedException { ByteBuf firstByteBuf = Unpooled.wrappedBuffer("ls\r".getBytes()); ByteBuf secondByteBuf = Unpooled.wrappedBuffer("\nls\r\n".getBytes()); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java index 40ca4fbce71..fea53d66acf 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoService.java @@ -66,4 +66,6 @@ public interface DemoService { String getPerson(Man man); String getRemoteApplicationName(); + + byte[] download(int size); } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java index 0d56e3e75b0..ec1d8c66532 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java @@ -18,6 +18,7 @@ import org.apache.dubbo.rpc.RpcContext; +import java.util.Arrays; import java.util.Map; import java.util.Set; @@ -126,4 +127,11 @@ public String getPerson(Man man) { public String getRemoteApplicationName() { return RpcContext.getContext().getRemoteApplicationName(); } + + @Override + public byte[] download(int size) { + byte[] bytes = new byte[size]; + Arrays.fill(bytes, (byte) 0); + return bytes; + } } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java b/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java index aa9a17ba086..6252e95aa9e 100644 --- a/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java +++ b/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/DubboHandlerRegistry.java @@ -36,7 +36,8 @@ public class DubboHandlerRegistry extends HandlerRegistry { private final Map services = new ConcurrentHashMap<>(); private final Map> methods = new ConcurrentHashMap<>(); - public DubboHandlerRegistry() {} + public DubboHandlerRegistry() { + } /** * Returns the service definitions in this registry. @@ -63,8 +64,10 @@ void addService(BindableService bindableService, String key) { void removeService(String serviceKey) { ServerServiceDefinition service = services.remove(serviceKey); - for (ServerMethodDefinition method : service.getMethods()) { - methods.remove(method.getMethodDescriptor().getFullMethodName(), method); + if (null != service) { + for (ServerMethodDefinition method : service.getMethods()) { + methods.remove(method.getMethodDescriptor().getFullMethodName(), method); + } } } } diff --git a/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/GrpcProtocol.java b/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/GrpcProtocol.java index 1342a17e6c8..42c311505cb 100644 --- a/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/GrpcProtocol.java +++ b/dubbo-rpc/dubbo-rpc-grpc/src/main/java/org/apache/dubbo/rpc/protocol/grpc/GrpcProtocol.java @@ -17,8 +17,6 @@ package org.apache.dubbo.rpc.protocol.grpc; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.config.ReferenceConfigBase; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.ProtocolServer; @@ -46,8 +44,6 @@ */ public class GrpcProtocol extends AbstractProxyProtocol { - private static final Logger logger = LoggerFactory.getLogger(GrpcProtocol.class); - public final static int DEFAULT_PORT = 50051; /* */ diff --git a/dubbo-rpc/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java b/dubbo-rpc/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java index a9bf02e0eff..7ee98b3722d 100644 --- a/dubbo-rpc/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java @@ -19,6 +19,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.beanutil.JavaBeanDescriptor; import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil; +import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.serialize.ObjectOutput; @@ -32,8 +33,8 @@ import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.protocol.hessian.HessianServiceImpl.MyException; - import org.apache.dubbo.rpc.service.GenericService; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -86,6 +87,8 @@ public void testGenericInvoke() { @Test public void testGenericInvokeWithNativeJava() throws IOException, ClassNotFoundException { + // temporary enable native java generic serialize + System.setProperty(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, "true"); HessianServiceImpl server = new HessianServiceImpl(); Assertions.assertFalse(server.isCalled()); ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); @@ -110,6 +113,7 @@ public void testGenericInvokeWithNativeJava() throws IOException, ClassNotFoundE Assertions.assertEquals("Hello, haha", objectInput.readObject()); invoker.destroy(); exporter.unexport(); + System.clearProperty(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE); } @Test diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java index 452d459e587..50d68c910d0 100644 --- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java +++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmExporter.java @@ -1,44 +1,42 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.dubbo.rpc.protocol.injvm; - -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.protocol.AbstractExporter; -import org.apache.dubbo.rpc.protocol.DelegateExporterMap; - -/** - * InjvmExporter - */ -class InjvmExporter extends AbstractExporter { - - private final String key; - - private final DelegateExporterMap delegateExporterMap; - - InjvmExporter(Invoker invoker, String key, DelegateExporterMap delegateExporterMap) { - super(invoker); - this.key = key; - this.delegateExporterMap = delegateExporterMap; - } - - @Override - public void unexport() { - super.unexport(); - delegateExporterMap.removeExportMap(key, this); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.injvm; + +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.protocol.AbstractExporter; +import org.apache.dubbo.rpc.protocol.DelegateExporterMap; + +/** + * InjvmExporter + */ +class InjvmExporter extends AbstractExporter { + + private final String key; + + private final DelegateExporterMap delegateExporterMap; + + InjvmExporter(Invoker invoker, String key, DelegateExporterMap delegateExporterMap) { + super(invoker); + this.key = key; + this.delegateExporterMap = delegateExporterMap; + } + + @Override + public void afterUnExport() { + delegateExporterMap.removeExportMap(key, this); + } +} diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java index 0b71efce933..521d7f3019c 100644 --- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java +++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.protocol.injvm; import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Constants; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invocation; import org.apache.dubbo.rpc.Result; @@ -59,6 +60,12 @@ public Result doInvoke(Invocation invocation) throws Throwable { throw new RpcException("Service [" + key + "] not found."); } RpcContext.getContext().setRemoteAddress(LOCALHOST_VALUE, 0); + // Solve local exposure, the server opens the token, and the client call fails. + URL serverURL = exporter.getInvoker().getUrl(); + boolean serverHasToken = serverURL.hasParameter(Constants.TOKEN_KEY); + if (serverHasToken) { + invocation.setAttachment(Constants.TOKEN_KEY, serverURL.getParameter(Constants.TOKEN_KEY)); + } return exporter.getInvoker().invoke(invocation); } } diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java index d67b37bfdb2..546766bd02e 100644 --- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java +++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocol.java @@ -26,7 +26,10 @@ import org.apache.dubbo.rpc.protocol.AbstractProtocol; import org.apache.dubbo.rpc.protocol.DelegateExporterMap; import org.apache.dubbo.rpc.support.ProtocolUtils; +import java.util.Map; +import static org.apache.dubbo.common.constants.CommonConstants.BROADCAST_CLUSTER; +import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY; import static org.apache.dubbo.rpc.Constants.SCOPE_KEY; import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL; import static org.apache.dubbo.rpc.Constants.SCOPE_REMOTE; @@ -112,6 +115,11 @@ public boolean isInjvmRefer(URL url) { // generic invocation is not local reference return false; } else if (getExporter(this, url) != null) { + // Broadcast cluster means that multiple machines will be called, + // which is not converted to injvm protocol at this time. + if (BROADCAST_CLUSTER.equalsIgnoreCase(url.getParameter(CLUSTER_KEY))) { + return false; + } // by default, go through local reference if there's the service exposed locally return true; } else { diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java index 59f098b0fd4..1ce0f25d088 100644 --- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmProtocolTest.java @@ -108,6 +108,17 @@ public Collection> getExporters() { } + @Test + public void testLocalProtocolWithToken() throws Exception { + DemoService service = new DemoServiceImpl(); + Invoker invoker = proxy.getInvoker(service, DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService?token=abc").addParameter(INTERFACE_KEY, DemoService.class.getName())); + assertTrue(invoker.isAvailable()); + Exporter exporter = protocol.export(invoker); + exporters.add(exporter); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService").addParameter(INTERFACE_KEY, DemoService.class.getName()))); + assertEquals(service.getSize(new String[]{"", "", ""}), 3); + } + @Test public void testIsInjvmRefer() throws Exception { DemoService service = new DemoServiceImpl(); @@ -135,6 +146,8 @@ public void testIsInjvmRefer() throws Exception { url = URL.valueOf("fake://127.0.0.1/TestService").addParameter(GENERIC_KEY, true); assertFalse(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url)); + url = URL.valueOf("fake://127.0.0.1/TestService").addParameter("cluster", "broadcast"); + assertFalse(InjvmProtocol.getInjvmProtocol().isInjvmRefer(url)); } diff --git a/dubbo-rpc/dubbo-rpc-native-thrift/src/test/java/org/apache/dubbo/rpc/protocol/nativethrift/DemoService.java b/dubbo-rpc/dubbo-rpc-native-thrift/src/test/java/org/apache/dubbo/rpc/protocol/nativethrift/DemoService.java index 97572f1c3b9..7e1de88dd97 100644 --- a/dubbo-rpc/dubbo-rpc-native-thrift/src/test/java/org/apache/dubbo/rpc/protocol/nativethrift/DemoService.java +++ b/dubbo-rpc/dubbo-rpc-native-thrift/src/test/java/org/apache/dubbo/rpc/protocol/nativethrift/DemoService.java @@ -3680,8 +3680,6 @@ public boolean equals(Object that) { public boolean equals(timeOut_result that) { if (that == null) return false; - if (this == that) - return true; return true; } @@ -3930,8 +3928,6 @@ public boolean equals(Object that) { public boolean equals(customException_args that) { if (that == null) return false; - if (this == that) - return true; return true; } diff --git a/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java b/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java index b5081887c2c..67dcd7b387f 100644 --- a/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java +++ b/dubbo-rpc/dubbo-rpc-redis/src/main/java/org/apache/dubbo/rpc/protocol/redis/RedisProtocol.java @@ -43,7 +43,6 @@ import java.io.IOException; import java.net.SocketTimeoutException; import java.util.Map; -import java.util.concurrent.TimeoutException; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; @@ -150,7 +149,7 @@ protected Result doInvoke(Invocation invocation) throws Throwable { } } catch (Throwable t) { RpcException re = new RpcException("Failed to invoke redis service method. interface: " + type.getName() + ", method: " + invocation.getMethodName() + ", url: " + url + ", cause: " + t.getMessage(), t); - if (t instanceof TimeoutException || t instanceof SocketTimeoutException) { + if (t instanceof SocketTimeoutException) { re.setCode(RpcException.TIMEOUT_EXCEPTION); } else if (t instanceof JedisConnectionException || t instanceof IOException) { re.setCode(RpcException.NETWORK_EXCEPTION); diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/test/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocolTest.java b/dubbo-rpc/dubbo-rpc-rmi/src/test/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocolTest.java index a1b9af26344..ab4e57e1472 100644 --- a/dubbo-rpc/dubbo-rpc-rmi/src/test/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-rmi/src/test/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocolTest.java @@ -38,7 +38,6 @@ public class RmiProtocolTest { private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); - int availablePort = NetUtils.getAvailablePort(); /* @Test @@ -49,6 +48,7 @@ public void test_getRemoteClass() throws Exception { */ @Test public void testRmiProtocolTimeout() throws Exception { + int availablePort = NetUtils.getAvailablePort(); System.setProperty("sun.rmi.transport.tcp.responseTimeout", "1000"); DemoService service = new DemoServiceImpl(); Exporter rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:" + availablePort + "/TestService"))); @@ -68,6 +68,7 @@ public void testRmiProtocolTimeout() throws Exception { @Test public void testRmiProtocol() throws Exception { { + int availablePort = NetUtils.getAvailablePort(); DemoService service = new DemoServiceImpl(); Exporter rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:" + availablePort + "/TestService"))); @@ -99,6 +100,7 @@ public void testRmiProtocol() throws Exception { @Disabled @Test public void testRmiProtocol_echoService() throws Exception { + int availablePort = NetUtils.getAvailablePort(); DemoService service = new DemoServiceImpl(); Exporter rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:" + availablePort + "/TestService"))); @@ -110,6 +112,7 @@ public void testRmiProtocol_echoService() throws Exception { rpcExporter.unexport(); + availablePort = NetUtils.getAvailablePort(); RemoteService remoteService = new RemoteServiceImpl(); rpcExporter = protocol.export(proxy.getInvoker(remoteService, RemoteService.class, URL.valueOf("rmi://127.0.0.1:" + availablePort + "/remoteService"))); @@ -124,6 +127,7 @@ public void testRmiProtocol_echoService() throws Exception { @Test public void testGenericInvoke() { + int availablePort = NetUtils.getAvailablePort(); DemoService service = new DemoServiceImpl(); URL url = URL.valueOf("rmi://127.0.0.1:" + availablePort + "/" + DemoService.class.getName() + "?release=2.7.0"); Exporter exporter = protocol.export(proxy.getInvoker(service, DemoService.class, url)); @@ -138,6 +142,7 @@ public void testGenericInvoke() { @Test public void testRemoteApplicationName() throws Exception { + int availablePort = NetUtils.getAvailablePort(); DemoService service = new DemoServiceImpl(); URL url = URL.valueOf("rmi://127.0.0.1:" + availablePort + "/TestService?release=2.7.0").addParameter("application", "consumer"); Exporter rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, url)); diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java index 5f58ae7e223..dad896cc03c 100644 --- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java +++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.ClassUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec2; @@ -350,7 +351,7 @@ private Object decode(TProtocol protocol) try { field = clazz.getDeclaredField(fieldIdEnum.getFieldName()); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); } catch (NoSuchFieldException e) { throw new RpcException(RpcException.SERIALIZATION_EXCEPTION, e.getMessage(), e); } diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/$__ClassNameTestDubboStub.java b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/$__ClassNameTestDubboStub.java index c7f92d7b5e0..348f151df37 100644 --- a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/$__ClassNameTestDubboStub.java +++ b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/$__ClassNameTestDubboStub.java @@ -35,7 +35,7 @@ public class $__ClassNameTestDubboStub { public interface Iface { - public String echo(String arg); + String echo(String arg); } diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ServiceMethodNotFoundTest.java b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ServiceMethodNotFoundTest.java index bb6857603ee..e58623a5705 100644 --- a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ServiceMethodNotFoundTest.java +++ b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ServiceMethodNotFoundTest.java @@ -17,6 +17,7 @@ package org.apache.dubbo.rpc.protocol.thrift; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.ReflectUtils; import org.apache.dubbo.rpc.gen.dubbo.$__DemoStub; import org.apache.dubbo.rpc.gen.dubbo.Demo; import org.apache.dubbo.rpc.protocol.thrift.ext.MultiServiceProcessor; @@ -46,7 +47,7 @@ protected void init() throws Exception { // for test Field field = processor.getClass().getSuperclass().getDeclaredField("processMap"); - field.setAccessible(true); + ReflectUtils.makeAccessible(field); Object obj = field.get(processor); @@ -132,7 +133,7 @@ public void testServiceMethodNotFound() throws Exception { String arg = "Hello, World!"; invocation.setArguments( new Object[] { arg } ); - + invocation.setAttachment(Constants.INTERFACE_KEY, DemoImpl.class.getName()); Result result = invoker.invoke( invocation ); diff --git a/dubbo-rpc/dubbo-rpc-webservice/src/test/java/org/apache/dubbo/rpc/protocol/webservice/WebserviceProtocolTest.java b/dubbo-rpc/dubbo-rpc-webservice/src/test/java/org/apache/dubbo/rpc/protocol/webservice/WebserviceProtocolTest.java index da18ba49604..e3d15eb3ab5 100644 --- a/dubbo-rpc/dubbo-rpc-webservice/src/test/java/org/apache/dubbo/rpc/protocol/webservice/WebserviceProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-webservice/src/test/java/org/apache/dubbo/rpc/protocol/webservice/WebserviceProtocolTest.java @@ -69,7 +69,7 @@ public void testWebserviceProtocol() throws Exception { System.out.println(object); assertEquals("webservice://127.0.0.1:" + port + "/org.apache.dubbo.rpc.protocol.webservice.DemoService:invoke", object); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (int i = 0; i < 1024 * 32 + 32; i++) buf.append('A'); assertEquals(32800, service.stringLength(buf.toString())); diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java index 23a77835e9c..2729e6ed375 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common.serialize.hessian2; +import org.apache.dubbo.common.serialize.Cleanable; import org.apache.dubbo.common.serialize.ObjectInput; import org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer; @@ -28,7 +29,7 @@ /** * Hessian2 object input implementation */ -public class Hessian2ObjectInput implements ObjectInput { +public class Hessian2ObjectInput implements ObjectInput, Cleanable { private static ThreadLocal INPUT_TL = ThreadLocal.withInitial(() -> { Hessian2Input h2i = new Hessian2Input(null); @@ -109,4 +110,11 @@ public T readObject(Class cls, Type type) throws IOException, ClassNotFou public InputStream readInputStream() throws IOException { return mH2i.readInputStream(); } + + @Override + public void cleanup() { + if(mH2i != null) { + mH2i.reset(); + } + } } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java index 9844415bbdf..874a2c8b21b 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectOutput.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.common.serialize.hessian2; +import org.apache.dubbo.common.serialize.Cleanable; import org.apache.dubbo.common.serialize.ObjectOutput; import org.apache.dubbo.common.serialize.hessian2.dubbo.Hessian2FactoryInitializer; @@ -27,7 +28,7 @@ /** * Hessian2 object output implementation */ -public class Hessian2ObjectOutput implements ObjectOutput { +public class Hessian2ObjectOutput implements ObjectOutput, Cleanable { private static ThreadLocal OUTPUT_TL = ThreadLocal.withInitial(() -> { Hessian2Output h2o = new Hessian2Output(null); @@ -106,4 +107,11 @@ public void flushBuffer() throws IOException { public OutputStream getOutputStream() throws IOException { return mH2o.getBytesOutputStream(); } + + @Override + public void cleanup() { + if(mH2o != null) { + mH2o.reset(); + } + } } diff --git a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java index 69e4b35b16f..f030186690a 100644 --- a/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java +++ b/dubbo-serialization/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/CompatibleKryo.java @@ -18,7 +18,7 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.serialize.kryo.utils.ReflectionUtils; +import org.apache.dubbo.common.utils.ReflectUtils; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; @@ -43,7 +43,7 @@ public Serializer getDefaultSerializer(Class type) { * default to the default serializer. * It is the responsibility of kryo to handle with every standard jdk classes, so we will just escape these classes. */ - if (!ReflectionUtils.isJdk(type) && !type.isArray() && !type.isEnum() && !ReflectionUtils.checkZeroArgConstructor(type)) { + if (!ReflectUtils.isJdk(type) && !type.isArray() && !type.isEnum() && !ReflectUtils.checkZeroArgConstructor(type)) { if (logger.isWarnEnabled()) { logger.warn(type + " has no zero-arg constructor and this will affect the serialization performance"); } diff --git a/dubbo-serialization/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerialization.java b/dubbo-serialization/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerialization.java index 0d2fae628c9..964797ef156 100644 --- a/dubbo-serialization/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerialization.java +++ b/dubbo-serialization/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerialization.java @@ -27,7 +27,7 @@ import static org.apache.dubbo.common.serialize.Constants.PROTOBUF_JSON_SERIALIZATION_ID; /** - * This serizalization is use for google protobuf generic reference. + * This serialization is use for google protobuf generic reference. * The entity be transported between client and server by json string. */ public class GenericProtobufJsonSerialization implements Serialization { diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/JMX_HealthEndpoint.png b/dubbo-spring-boot/dubbo-spring-boot-actuator/JMX_HealthEndpoint.png new file mode 100644 index 00000000000..18e9f92abb1 Binary files /dev/null and b/dubbo-spring-boot/dubbo-spring-boot-actuator/JMX_HealthEndpoint.png differ diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/README.md b/dubbo-spring-boot/dubbo-spring-boot-actuator/README.md new file mode 100644 index 00000000000..3c478d9ba6e --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/README.md @@ -0,0 +1,499 @@ +# Dubbo Spring Boot Production-Ready + +`dubbo-spring-boot-actuator` provides production-ready features (e.g. [health checks](#health-checks), [endpoints](#endpoints), and [externalized configuration](#externalized-configuration)). + + + +## Content + +1. [Main Content](https://github.com/apache/dubbo-spring-boot-project) +2. [Integrate with Maven](#integrate-with-maven) +3. [Health Checks](#health-checks) +4. [Endpoints](#endpoints) +5. [Externalized Configuration](#externalized-configuration) + + + + +## Integrate with Maven + +You can introduce the latest `dubbo-spring-boot-actuator` to your project by adding the following dependency to your pom.xml +```xml + + org.apache.dubbo + dubbo-spring-boot-actuator + 2.7.4.1 + +``` + +If your project failed to resolve the dependency, try to add the following repository: +```xml + + + apache.snapshots.https + Apache Development Snapshot Repository + https://repository.apache.org/content/repositories/snapshots + + false + + + true + + + +``` + + + +## Health Checks + +`dubbo-spring-boot-actuator` supports the standard Spring Boot `HealthIndicator` as a production-ready feature , which will be aggregated into Spring Boot's `Health` and exported on `HealthEndpoint` that works MVC (Spring Web MVC) and JMX (Java Management Extensions) both if they are available. + + + +### Web Endpoint : `/health` + + + +Suppose a Spring Boot Web application did not specify `management.server.port`, you can access http://localhost:8080/actuator/health via Web Client and will get a response with JSON format is like below : + +```json +{ + "status": "UP", + "dubbo": { + "status": "UP", + "memory": { + "source": "management.health.dubbo.status.defaults", + "status": { + "level": "OK", + "message": "max:3641M,total:383M,used:92M,free:291M", + "description": null + } + }, + "load": { + "source": "management.health.dubbo.status.extras", + "status": { + "level": "OK", + "message": "load:1.73583984375,cpu:8", + "description": null + } + }, + "threadpool": { + "source": "management.health.dubbo.status.extras", + "status": { + "level": "OK", + "message": "Pool status:OK, max:200, core:200, largest:0, active:0, task:0, service port: 12345", + "description": null + } + }, + "server": { + "source": "dubbo@ProtocolConfig.getStatus()", + "status": { + "level": "OK", + "message": "/192.168.1.103:12345(clients:0)", + "description": null + } + } + } + // ignore others +} +``` + + + `memory`, `load`, `threadpool` and `server` are Dubbo's build-in `StatusChecker`s in above example. + Dubbo allows the application to extend `StatusChecker`'s SPI. + +Default , `memory` and `load` will be added into Dubbo's `HealthIndicator` , it could be overridden by +externalized configuration [`StatusChecker`'s defaults](#statuschecker-defaults). + + + +### JMX Endpoint : `Health` + + + +`Health` is a JMX (Java Management Extensions) Endpoint with ObjectName `org.springframework.boot:type=Endpoint,name=Health` , it can be managed by JMX agent ,e.g. JDK tools : `jconsole` and so on. + +![](JMX_HealthEndpoint.png) + + + +### Build-in `StatusChecker`s + + + + `META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker` declares Build-in `StatusChecker`s as follow : + +```properties +registry=org.apache.dubbo.registry.status.RegistryStatusChecker +spring=org.apache.dubbo.config.spring.status.SpringStatusChecker +datasource=org.apache.dubbo.config.spring.status.DataSourceStatusChecker +memory=org.apache.dubbo.common.status.support.MemoryStatusChecker +load=org.apache.dubbo.common.status.support.LoadStatusChecker +server=org.apache.dubbo.rpc.protocol.dubbo.status.ServerStatusChecker +threadpool=org.apache.dubbo.rpc.protocol.dubbo.status.ThreadPoolStatusChecker +``` + + + +The property key that is name of `StatusChecker` can be a valid value of `management.health.dubbo.status.*` in externalized configuration. + + + +## Endpoints + + + +Actuator endpoint `dubbo` supports Actuator Endpoints : + +| ID | Enabled | HTTP URI | HTTP Method | Description | Content Type | +| ------------------- | ----------- | ----------------------------------- | ------------------ | ------------------ | ------------------ | +| `dubbo` | `true` | `/actuator/dubbo` | `GET` | Exposes Dubbo's meta data | `application/json` | +| `dubboproperties` | `true` | `/actuator/dubbo/properties` | `GET` | Exposes all Dubbo's Properties | `application/json` | +| `dubboservices` | `false` | `/dubbo/services` | `GET` | Exposes all Dubbo's `ServiceBean` | `application/json` | +| `dubboreferences` | `false` | `/actuator/dubbo/references` | `GET` | Exposes all Dubbo's `ReferenceBean` | `application/json` | +| `dubboconfigs` | `true` | `/actuator/dubbo/configs` | `GET` | Exposes all Dubbo's `*Config` | `application/json` | +| `dubboshutdown` | `false` | `/actuator/dubbo/shutdown` | `POST` | Shutdown Dubbo services | `application/json` | + + + + + +### Web Endpoints + + + +#### `/actuator/dubbo` + +`/dubbo` exposes Dubbo's meta data : + +```json +{ + "timestamp": 1516623290166, + "versions": { + "dubbo-spring-boot": "2.7.5", + "dubbo": "2.7.5" + }, + "urls": { + "dubbo": "https://github.com/apache/dubbo/", + "google-group": "dev@dubbo.apache.org", + "github": "https://github.com/apache/dubbo-spring-boot-project", + "issues": "https://github.com/apache/dubbo-spring-boot-project/issues", + "git": "https://github.com/apache/dubbo-spring-boot-project.git" + } +} +``` + +### + +#### `/actuator/dubbo/properties` + +`/actuator/dubbo/properties` exposes all Dubbo's Properties from Spring Boot Externalized Configuration (a.k.a `PropertySources`) : + +```json +{ + "dubbo.application.id": "dubbo-provider-demo", + "dubbo.application.name": "dubbo-provider-demo", + "dubbo.application.qos-enable": "false", + "dubbo.application.qos-port": "33333", + "dubbo.protocol.id": "dubbo", + "dubbo.protocol.name": "dubbo", + "dubbo.protocol.port": "12345", + "dubbo.registry.address": "N/A", + "dubbo.registry.id": "my-registry", + "dubbo.scan.basePackages": "org.apache.dubbo.spring.boot.sample.provider.service" +} +``` + +The structure of JSON is simple Key-Value format , the key is property name as and the value is property value. + + + + + +#### `/actuator/dubbo/services` + +`/actuator/dubbo/services` exposes all Dubbo's `ServiceBean` that are declared via `` or `@Service` present in Spring `ApplicationContext` : + +```json +{ + "ServiceBean@org.apache.dubbo.spring.boot.sample.api.DemoService#defaultDemoService": { + "accesslog": null, + "actives": null, + "cache": null, + "callbacks": null, + "class": "org.apache.dubbo.config.spring.ServiceBean", + "cluster": null, + "connections": null, + "delay": null, + "document": null, + "executes": null, + "export": null, + "exported": true, + "filter": "", + "generic": "false", + "group": null, + "id": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "interface": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "interfaceClass": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "layer": null, + "listener": "", + "loadbalance": null, + "local": null, + "merger": null, + "mock": null, + "onconnect": null, + "ondisconnect": null, + "owner": null, + "path": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "proxy": null, + "retries": null, + "scope": null, + "sent": null, + "stub": null, + "timeout": null, + "token": null, + "unexported": false, + "uniqueServiceName": "org.apache.dubbo.spring.boot.sample.api.DemoService:1.0.0", + "validation": null, + "version": "1.0.0", + "warmup": null, + "weight": null, + "serviceClass": "DefaultDemoService" + } +} +``` + +The key is the Bean name of `ServiceBean` , `ServiceBean`'s properties compose value. + + + +#### `/actuator/dubbo/references` + +`/actuator/dubbo/references` exposes all Dubbo's `ReferenceBean` that are declared via `@Reference` annotating on `Field` or `Method ` : + +```json +{ + "private org.apache.dubbo.spring.boot.sample.api.DemoService org.apache.dubbo.spring.boot.sample.consumer.controller.DemoConsumerController.demoService": { + "actives": null, + "cache": null, + "callbacks": null, + "class": "org.apache.dubbo.config.spring.ReferenceBean", + "client": null, + "cluster": null, + "connections": null, + "filter": "", + "generic": null, + "group": null, + "id": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "interface": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "interfaceClass": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "layer": null, + "lazy": null, + "listener": "", + "loadbalance": null, + "local": null, + "merger": null, + "mock": null, + "objectType": "org.apache.dubbo.spring.boot.sample.api.DemoService", + "onconnect": null, + "ondisconnect": null, + "owner": null, + "protocol": null, + "proxy": null, + "reconnect": null, + "retries": null, + "scope": null, + "sent": null, + "singleton": true, + "sticky": null, + "stub": null, + "stubevent": null, + "timeout": null, + "uniqueServiceName": "org.apache.dubbo.spring.boot.sample.api.DemoService:1.0.0", + "url": "dubbo://localhost:12345", + "validation": null, + "version": "1.0.0", + "invoker": { + "class": "org.apache.dubbo.common.bytecode.proxy0" + } + } +} +``` + +The key is the string presentation of `@Reference` `Field` or `Method ` , `ReferenceBean`'s properties compose value. + + + +#### `/actuator/dubbo/configs` + + `/actuator/dubbo/configs` exposes all Dubbo's `*Config` : + +```json +{ + "ApplicationConfig": { + "dubbo-consumer-demo": { + "architecture": null, + "class": "org.apache.dubbo.config.ApplicationConfig", + "compiler": null, + "dumpDirectory": null, + "environment": null, + "id": "dubbo-consumer-demo", + "logger": null, + "name": "dubbo-consumer-demo", + "organization": null, + "owner": null, + "version": null + } + }, + "ConsumerConfig": { + + }, + "MethodConfig": { + + }, + "ModuleConfig": { + + }, + "MonitorConfig": { + + }, + "ProtocolConfig": { + "dubbo": { + "accepts": null, + "accesslog": null, + "buffer": null, + "charset": null, + "class": "org.apache.dubbo.config.ProtocolConfig", + "client": null, + "codec": null, + "contextpath": null, + "dispatcher": null, + "dispather": null, + "exchanger": null, + "heartbeat": null, + "host": null, + "id": "dubbo", + "iothreads": null, + "name": "dubbo", + "networker": null, + "path": null, + "payload": null, + "port": 12345, + "prompt": null, + "queues": null, + "serialization": null, + "server": null, + "status": null, + "telnet": null, + "threadpool": null, + "threads": null, + "transporter": null + } + }, + "ProviderConfig": { + + }, + "ReferenceConfig": { + + }, + "RegistryConfig": { + + }, + "ServiceConfig": { + + } +} +``` + +The key is the simple name of Dubbo `*Config` Class , the value is`*Config` Beans' Name-Properties Map. + + + +#### `/actuator/dubbo/shutdown` + +`/actuator/dubbo/shutdown` shutdowns Dubbo's components including registries, protocols, services and references : + +```json +{ + "shutdown.count": { + "registries": 0, + "protocols": 1, + "services": 0, + "references": 1 + } +} +``` + +"shutdown.count" means the count of shutdown of Dubbo's components , and the value indicates how many components have been shutdown. + + + +## Externalized Configuration + + + +### `StatusChecker` Defaults + + + +`management.health.dubbo.status.defaults` is a property name for setting names of `StatusChecker`s , its value is allowed to multiple-values , for example : + +```properties +management.health.dubbo.status.defaults = registry,memory,load +``` + + + +#### Default Value + +The default value is : + +```properties +management.health.dubbo.status.defaults = memory,load +``` + + + +### `StatusChecker` Extras + + + +`management.health.dubbo.status.extras` is used to override the [ [`StatusChecker`'s defaults]](#statuschecker-defaults) , the multiple-values is delimited by comma : + +```properties +management.health.dubbo.status.extras = load,threadpool +``` + + + +### Health Checks Enabled + + + +`management.health.dubbo.enabled` is a enabled configuration to turn on or off health checks feature, its' default is `true`. + + If you'd like to disable health checks , you chould apply `management.health.dubbo.enabled` to be `false`: + +```properties +management.health.dubbo.enabled = false +``` + + + +### Endpoints Enabled + + + +Dubbo Spring Boot providers actuator endpoints , however some of them are disable. If you'd like to enable them , please add following properties into externalized configuration : + +```properties +# Enables Dubbo All Endpoints +management.endpoint.dubbo.enabled = true +management.endpoint.dubboshutdown.enabled = true +management.endpoint.dubboconfigs.enabled = true +management.endpoint.dubboservices.enabled = true +management.endpoint.dubboreferences.enabled = true +management.endpoint.dubboproperties.enabled = true +``` + diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml new file mode 100644 index 00000000000..5c066f4b13c --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml @@ -0,0 +1,118 @@ + + + + + org.apache.dubbo + dubbo-spring-boot + ${revision} + ../pom.xml + + 4.0.0 + + dubbo-spring-boot-actuator + jar + Apache Dubbo Spring Boot Actuator + + + + + org.apache.dubbo + dubbo-spring-boot-actuator-compatible + ${revision} + + + + + org.springframework.boot + spring-boot-starter-web + true + + + + org.springframework.boot + spring-boot-starter-actuator + true + + + + org.springframework.boot + spring-boot-autoconfigure + true + + + + + org.apache.dubbo + dubbo-spring-boot-autoconfigure + ${revision} + + + + + org.apache.dubbo + dubbo + ${revision} + + + + org.apache.dubbo + dubbo-common + ${revision} + true + + + + org.apache.dubbo + dubbo-config-spring + ${revision} + true + + + + org.apache.dubbo + dubbo-remoting-netty4 + ${revision} + true + + + + org.apache.dubbo + dubbo-serialization-hessian2 + ${revision} + true + + + + org.apache.dubbo + dubbo-rpc-dubbo + ${revision} + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfiguration.java new file mode 100644 index 00000000000..7ae8b848de0 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfiguration.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.autoconfigure; + +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboConfigsMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboPropertiesMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboReferencesMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboServicesMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboShutdownEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.condition.CompatibleConditionalOnEnabledEndpoint; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +/** + * Dubbo {@link Endpoint @Endpoint} Auto-{@link Configuration} for Spring Boot Actuator 2.0 + * + * @see Endpoint + * @see Configuration + * @since 2.7.0 + */ +@Configuration +@PropertySource( + name = "Dubbo Endpoints Default Properties", + value = "classpath:/META-INF/dubbo-endpoints-default.properties") +public class DubboEndpointAnnotationAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + @CompatibleConditionalOnEnabledEndpoint + public DubboMetadataEndpoint dubboEndpoint() { + return new DubboMetadataEndpoint(); + } + + @Bean + @ConditionalOnMissingBean + @CompatibleConditionalOnEnabledEndpoint + public DubboConfigsMetadataEndpoint dubboConfigsMetadataEndpoint() { + return new DubboConfigsMetadataEndpoint(); + } + + @Bean + @ConditionalOnMissingBean + @CompatibleConditionalOnEnabledEndpoint + public DubboPropertiesMetadataEndpoint dubboPropertiesEndpoint() { + return new DubboPropertiesMetadataEndpoint(); + } + + @Bean + @ConditionalOnMissingBean + @CompatibleConditionalOnEnabledEndpoint + public DubboReferencesMetadataEndpoint dubboReferencesMetadataEndpoint() { + return new DubboReferencesMetadataEndpoint(); + } + + @Bean + @ConditionalOnMissingBean + @CompatibleConditionalOnEnabledEndpoint + public DubboServicesMetadataEndpoint dubboServicesMetadataEndpoint() { + return new DubboServicesMetadataEndpoint(); + } + + @Bean + @ConditionalOnMissingBean + @CompatibleConditionalOnEnabledEndpoint + public DubboShutdownEndpoint dubboShutdownEndpoint() { + return new DubboShutdownEndpoint(); + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboConfigsMetadataEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboConfigsMetadataEndpoint.java new file mode 100644 index 00000000000..b861e5ccb07 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboConfigsMetadataEndpoint.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.AbstractDubboMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboConfigsMetadata; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import java.util.Map; + +/** + * Dubbo Configs Metadata {@link Endpoint} + * + * @since 2.7.0 + */ +@Endpoint(id = "dubboconfigs") +public class DubboConfigsMetadataEndpoint extends AbstractDubboMetadata { + + @Autowired + private DubboConfigsMetadata dubboConfigsMetadata; + + @ReadOperation + public Map>> configs() { + return dubboConfigsMetadata.configs(); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboMetadataEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboMetadataEndpoint.java new file mode 100644 index 00000000000..edf732840fe --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboMetadataEndpoint.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboMetadata; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import java.util.Map; + +/** + * Actuator {@link Endpoint} to expose Dubbo Meta Data + * + * @see Endpoint + * @since 2.7.0 + */ +@Endpoint(id = "dubbo") +public class DubboMetadataEndpoint { + + @Autowired + private DubboMetadata dubboMetadata; + + @ReadOperation + public Map invoke() { + return dubboMetadata.invoke(); + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboPropertiesMetadataEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboPropertiesMetadataEndpoint.java new file mode 100644 index 00000000000..4295113f75b --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboPropertiesMetadataEndpoint.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.AbstractDubboMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboPropertiesMetadata; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import java.util.SortedMap; + +/** + * Dubbo Properties {@link Endpoint} + * + * @since 2.7.0 + */ +@Endpoint(id = "dubboproperties") +public class DubboPropertiesMetadataEndpoint extends AbstractDubboMetadata { + + @Autowired + private DubboPropertiesMetadata dubboPropertiesMetadata; + + @ReadOperation + public SortedMap properties() { + return dubboPropertiesMetadata.properties(); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboReferencesMetadataEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboReferencesMetadataEndpoint.java new file mode 100644 index 00000000000..18fc09b273c --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboReferencesMetadataEndpoint.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.AbstractDubboMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboReferencesMetadata; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import java.util.Map; + +/** + * {@link DubboReference} Metadata {@link Endpoint} + * + * @since 2.7.0 + */ +@Endpoint(id = "dubboreferences") +public class DubboReferencesMetadataEndpoint extends AbstractDubboMetadata { + + @Autowired + private DubboReferencesMetadata dubboReferencesMetadata; + + @ReadOperation + public Map> references() { + return dubboReferencesMetadata.references(); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboServicesMetadataEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboServicesMetadataEndpoint.java new file mode 100644 index 00000000000..552e4b8c085 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboServicesMetadataEndpoint.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + +import org.apache.dubbo.config.annotation.DubboService; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.AbstractDubboMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboServicesMetadata; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import java.util.Map; + +/** + * {@link DubboService} Metadata {@link Endpoint} + * + * @since 2.7.0 + */ +@Endpoint(id = "dubboservices") +public class DubboServicesMetadataEndpoint extends AbstractDubboMetadata { + + @Autowired + private DubboServicesMetadata dubboServicesMetadata; + + @ReadOperation + public Map> services() { + return dubboServicesMetadata.services(); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboShutdownEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboShutdownEndpoint.java new file mode 100644 index 00000000000..e62b57ba1ba --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboShutdownEndpoint.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.AbstractDubboMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboShutdownMetadata; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; + +import java.util.Map; + +/** + * Dubbo Shutdown + * + * @since 2.7.0 + */ +@Endpoint(id = "dubboshutdown") +public class DubboShutdownEndpoint extends AbstractDubboMetadata { + + @Autowired + private DubboShutdownMetadata dubboShutdownMetadata; + + @WriteOperation + public Map shutdown() throws Exception { + return dubboShutdownMetadata.shutdown(); + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleConditionalOnEnabledEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleConditionalOnEnabledEndpoint.java new file mode 100644 index 00000000000..245498040d7 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleConditionalOnEnabledEndpoint.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.dubbo.spring.boot.actuate.endpoint.condition; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension; +import org.springframework.context.annotation.Conditional; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@link Conditional} that checks whether or not an endpoint is enabled, which is compatible with + * org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint ([2.0.x, 2.2.x]) + * org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint + * + * @see CompatibleOnEnabledEndpointCondition + * @since 2.7.7 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +@Documented +@Conditional(CompatibleOnEnabledEndpointCondition.class) +public @interface CompatibleConditionalOnEnabledEndpoint { + + /** + * The endpoint type that should be checked. Inferred when the return type of the + * {@code @Bean} method is either an {@link Endpoint @Endpoint} or an + * {@link EndpointExtension @EndpointExtension}. + * + * @return the endpoint type to check + */ + Class endpoint() default Void.class; +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleOnEnabledEndpointCondition.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleOnEnabledEndpointCondition.java new file mode 100644 index 00000000000..8de2a5682d3 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleOnEnabledEndpointCondition.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.dubbo.spring.boot.actuate.endpoint.condition; + +import org.springframework.beans.BeanUtils; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.ClassUtils; + +import java.util.stream.Stream; + +/** + * {@link Conditional} that checks whether or not an endpoint is enabled, which is compatible with + * org.springframework.boot.actuate.autoconfigure.endpoint.condition.OnEnabledEndpointCondition + * and org.springframework.boot.actuate.autoconfigure.endpoint.condition.OnAvailableEndpointCondition + * + * @see CompatibleConditionalOnEnabledEndpoint + * @since 2.7.7 + */ +class CompatibleOnEnabledEndpointCondition implements Condition { + + static String[] CONDITION_CLASS_NAMES = { + "org.springframework.boot.actuate.autoconfigure.endpoint.condition.OnAvailableEndpointCondition", // 2.2.0+ + "org.springframework.boot.actuate.autoconfigure.endpoint.condition.OnEnabledEndpointCondition" // [2.0.0 , 2.2.x] + }; + + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + ClassLoader classLoader = context.getClassLoader(); + + Condition condition = Stream.of(CONDITION_CLASS_NAMES) // Iterate class names + .filter(className -> ClassUtils.isPresent(className, classLoader)) // Search class existing or not by name + .findFirst() // Find the first candidate + .map(className -> ClassUtils.resolveClassName(className, classLoader)) // Resolve class name to Class + .filter(Condition.class::isAssignableFrom) // Accept the Condition implementation + .map(BeanUtils::instantiateClass) // Instantiate Class to be instance + .map(Condition.class::cast) // Cast the instance to be Condition one + .orElse(NegativeCondition.INSTANCE); // Or else get a negative condition + + return condition.matches(context, metadata); + } + + private static class NegativeCondition implements Condition { + + static final NegativeCondition INSTANCE = new NegativeCondition(); + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return false; + } + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/dubbo-endpoints-default.properties b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/dubbo-endpoints-default.properties new file mode 100644 index 00000000000..4df591ed791 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/dubbo-endpoints-default.properties @@ -0,0 +1,21 @@ +# Dubbo Endpoints Default Properties is loaded by @PropertySource with low order, +# those values of properties can be override by higher PropertySource +# @see DubboEndpointsAutoConfiguration + +# Set enabled for Dubbo Endpoints +management.endpoint.dubbo.enabled = true +management.endpoint.dubboshutdown.enabled = false +management.endpoint.dubboconfigs.enabled = true +management.endpoint.dubboservices.enabled = false +management.endpoint.dubboreferences.enabled = false +management.endpoint.dubboproperties.enabled = true + +# "management.endpoints.web.base-path" should not be configured in this file + +# Re-defines path-mapping of Dubbo Web Endpoints + +management.endpoints.web.path-mapping.dubboshutdown = dubbo/shutdown +management.endpoints.web.path-mapping.dubboconfigs = dubbo/configs +management.endpoints.web.path-mapping.dubboservices = dubbo/services +management.endpoints.web.path-mapping.dubboreferences = dubbo/references +management.endpoints.web.path-mapping.dubboproperties = dubbo/properties \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/spring.factories b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000000..5dc6c60c181 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboEndpointAnnotationAutoConfiguration \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfigurationTest.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfigurationTest.java new file mode 100644 index 00000000000..8ef34ca9a9d --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfigurationTest.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.dubbo.spring.boot.actuate.autoconfigure; + +import org.apache.dubbo.config.annotation.DubboService; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboConfigsMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboPropertiesMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboReferencesMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboServicesMetadataEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboShutdownEndpoint; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; +import java.util.SortedMap; +import java.util.function.Supplier; + +/** + * {@link DubboEndpointAnnotationAutoConfiguration} Test + * + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = { + DubboEndpointAnnotationAutoConfigurationTest.class + }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "dubbo.service.version = 1.0.0", + "dubbo.application.id = my-application", + "dubbo.application.name = dubbo-demo-application", + "dubbo.module.id = my-module", + "dubbo.module.name = dubbo-demo-module", + "dubbo.registry.id = my-registry", + "dubbo.registry.address = N/A", + "dubbo.protocol.id=my-protocol", + "dubbo.protocol.name=dubbo", + "dubbo.protocol.port=20880", + "dubbo.provider.id=my-provider", + "dubbo.provider.host=127.0.0.1", + "dubbo.scan.basePackages = org.apache.dubbo.spring.boot.actuate.autoconfigure", + "management.endpoint.dubbo.enabled = true", + "management.endpoint.dubboshutdown.enabled = true", + "management.endpoint.dubboconfigs.enabled = true", + "management.endpoint.dubboservices.enabled = true", + "management.endpoint.dubboreferences.enabled = true", + "management.endpoint.dubboproperties.enabled = true", + "management.endpoints.web.exposure.include = *", + }) +@EnableAutoConfiguration +public class DubboEndpointAnnotationAutoConfigurationTest { + + @Autowired + private DubboMetadataEndpoint dubboEndpoint; + + @Autowired + private DubboConfigsMetadataEndpoint dubboConfigsMetadataEndpoint; + + @Autowired + private DubboPropertiesMetadataEndpoint dubboPropertiesEndpoint; + + @Autowired + private DubboReferencesMetadataEndpoint dubboReferencesMetadataEndpoint; + + @Autowired + private DubboServicesMetadataEndpoint dubboServicesMetadataEndpoint; + + @Autowired + private DubboShutdownEndpoint dubboShutdownEndpoint; + + private RestTemplate restTemplate = new RestTemplate(); + + @Autowired + private ObjectMapper objectMapper; + + @Value("http://127.0.0.1:${local.management.port}${management.endpoints.web.base-path:/actuator}") + private String actuatorBaseURL; + + @Before + public void init() { + ApplicationModel.reset(); + } + + @After + public void destroy() { + ApplicationModel.reset(); + } + + @Test + public void testShutdown() throws Exception { + + Map value = dubboShutdownEndpoint.shutdown(); + + Map shutdownCounts = (Map) value.get("shutdown.count"); + + Assert.assertEquals(0, shutdownCounts.get("registries")); + Assert.assertEquals(1, shutdownCounts.get("protocols")); + Assert.assertEquals(1, shutdownCounts.get("services")); + Assert.assertEquals(0, shutdownCounts.get("references")); + + } + + @Test + public void testConfigs() { + + Map>> configsMap = dubboConfigsMetadataEndpoint.configs(); + + Map> beansMetadata = configsMap.get("ApplicationConfig"); + Assert.assertEquals("dubbo-demo-application", beansMetadata.get("my-application").get("name")); + + beansMetadata = configsMap.get("ConsumerConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("MethodConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("ModuleConfig"); + Assert.assertEquals("dubbo-demo-module", beansMetadata.get("my-module").get("name")); + + beansMetadata = configsMap.get("MonitorConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("ProtocolConfig"); + Assert.assertEquals("dubbo", beansMetadata.get("my-protocol").get("name")); + + beansMetadata = configsMap.get("ProviderConfig"); + Assert.assertEquals("127.0.0.1", beansMetadata.get("my-provider").get("host")); + + beansMetadata = configsMap.get("ReferenceConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("RegistryConfig"); + Assert.assertEquals("N/A", beansMetadata.get("my-registry").get("address")); + + beansMetadata = configsMap.get("ServiceConfig"); + Assert.assertFalse(beansMetadata.isEmpty()); + + } + + @Test + public void testServices() { + + Map> services = dubboServicesMetadataEndpoint.services(); + + Assert.assertEquals(1, services.size()); + + Map demoServiceMeta = services.get("ServiceBean:org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboEndpointAnnotationAutoConfigurationTest$DemoService:1.0.0"); + + Assert.assertEquals("1.0.0", demoServiceMeta.get("version")); + + } + + @Test + public void testReferences() { + + Map> references = dubboReferencesMetadataEndpoint.references(); + + Assert.assertTrue(references.isEmpty()); + + } + + @Test + public void testProperties() { + + SortedMap properties = dubboPropertiesEndpoint.properties(); + + Assert.assertEquals("my-application", properties.get("dubbo.application.id")); + Assert.assertEquals("dubbo-demo-application", properties.get("dubbo.application.name")); + Assert.assertEquals("my-module", properties.get("dubbo.module.id")); + Assert.assertEquals("dubbo-demo-module", properties.get("dubbo.module.name")); + Assert.assertEquals("my-registry", properties.get("dubbo.registry.id")); + Assert.assertEquals("N/A", properties.get("dubbo.registry.address")); + Assert.assertEquals("my-protocol", properties.get("dubbo.protocol.id")); + Assert.assertEquals("dubbo", properties.get("dubbo.protocol.name")); + Assert.assertEquals("20880", properties.get("dubbo.protocol.port")); + Assert.assertEquals("my-provider", properties.get("dubbo.provider.id")); + Assert.assertEquals("127.0.0.1", properties.get("dubbo.provider.host")); + Assert.assertEquals("org.apache.dubbo.spring.boot.actuate.autoconfigure", properties.get("dubbo.scan.basePackages")); + } + + @Test + public void testHttpEndpoints() throws JsonProcessingException { +// testHttpEndpoint("/dubbo", dubboEndpoint::invoke); + testHttpEndpoint("/dubbo/configs", dubboConfigsMetadataEndpoint::configs); + testHttpEndpoint("/dubbo/services", dubboServicesMetadataEndpoint::services); + testHttpEndpoint("/dubbo/references", dubboReferencesMetadataEndpoint::references); + testHttpEndpoint("/dubbo/properties", dubboPropertiesEndpoint::properties); + } + + private void testHttpEndpoint(String actuatorURI, Supplier resultsSupplier) throws JsonProcessingException { + String actuatorURL = actuatorBaseURL + actuatorURI; + String response = restTemplate.getForObject(actuatorURL, String.class); + Assert.assertEquals(objectMapper.writeValueAsString(resultsSupplier.get()), response); + } + + + interface DemoService { + String sayHello(String name); + } + + @DubboService( + version = "${dubbo.service.version}", + application = "${dubbo.application.id}", + protocol = "${dubbo.protocol.id}", + registry = "${dubbo.registry.id}" + ) + static class DefaultDemoService implements DemoService { + + public String sayHello(String name) { + return "Hello, " + name + " (from Spring Boot)"; + } + + } + + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java new file mode 100644 index 00000000000..7ae9e61c4fc --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.spring.boot.util.DubboUtils; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Map; + +import static org.apache.dubbo.common.Version.getVersion; + +/** + * {@link DubboMetadataEndpoint} Test + * + * @see DubboMetadataEndpoint + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = { + DubboMetadataEndpoint.class + }, + properties = { + "dubbo.application.name = dubbo-demo-application" + } +) +@EnableAutoConfiguration +public class DubboEndpointTest { + + + @Autowired + private DubboMetadataEndpoint dubboEndpoint; + + @Before + public void init() { + ApplicationModel.reset(); + } + + @After + public void destroy() { + ApplicationModel.reset(); + } + + @Test + public void testInvoke() { + + Map metadata = dubboEndpoint.invoke(); + + Assert.assertNotNull(metadata.get("timestamp")); + + Map versions = (Map) metadata.get("versions"); + Map urls = (Map) metadata.get("urls"); + + Assert.assertFalse(versions.isEmpty()); + Assert.assertFalse(urls.isEmpty()); + + Assert.assertEquals(getVersion(DubboUtils.class, "1.0.0"), versions.get("dubbo-spring-boot")); + Assert.assertEquals(getVersion(), versions.get("dubbo")); + + Assert.assertEquals("https://github.com/apache/dubbo", urls.get("dubbo")); + Assert.assertEquals("dev@dubbo.apache.org", urls.get("mailing-list")); + Assert.assertEquals("https://github.com/apache/dubbo-spring-boot-project", urls.get("github")); + Assert.assertEquals("https://github.com/apache/dubbo-spring-boot-project/issues", urls.get("issues")); + Assert.assertEquals("https://github.com/apache/dubbo-spring-boot-project.git", urls.get("git")); + + } + + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/README.md b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/README.md new file mode 100644 index 00000000000..b99cc473702 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/README.md @@ -0,0 +1,225 @@ +# Dubbo Spring Boot Auto-Configure + +`dubbo-spring-boot-autoconfigure` uses Spring Boot's `@EnableAutoConfiguration` which helps core Dubbo's components to be auto-configured by `DubboAutoConfiguration`. It reduces code, eliminates XML configuration. + + + +## Content + +1. [Main Content](https://github.com/apache/dubbo-spring-boot-project) +2. [Integrate with Maven](#integrate-with-maven) +3. [Auto Configuration](#auto-configuration) +4. [Externalized Configuration](#externalized-configuration) +5. [Dubbo Annotation-Driven (Chinese)](http://dubbo.apache.org/zh-cn/blog/dubbo-annotation-driven.html) +6. [Dubbo Externalized Configuration (Chinese)](http://dubbo.apache.org/zh-cn/blog/dubbo-externalized-configuration.html) + + + +## Integrate with Maven + +You can introduce the latest `dubbo-spring-boot-autoconfigure` to your project by adding the following dependency to your pom.xml + +```xml + + org.apache.dubbo + dubbo-spring-boot-autoconfigure + 2.7.4.1 + +``` + +If your project failed to resolve the dependency, try to add the following repository: +```xml + + + apache.snapshots.https + Apache Development Snapshot Repository + https://repository.apache.org/content/repositories/snapshots + + false + + + true + + + +``` + + + +## Auto Configuration + +Since `2.5.7` , Dubbo totally supports Annotation-Driven , core Dubbo's components that are registered and initialized in Spring application context , including exterialized configuration features. However , those features need to trigger in manual configuration , e.g `@DubboComponentScan` , `@EnableDubboConfig` or `@EnableDubbo`. + +> If you'd like to learn more , please read [Dubbo Annotation-Driven (Chinese)](http://dubbo.apache.org/zh-cn/blog/dubbo-annotation-driven.html) + + + +`dubbo-spring-boot-autoconfigure` uses Spring Boot's `@EnableAutoConfiguration` which helps core Dubbo's components to be auto-configured by `DubboAutoConfiguration`. It reduces code, eliminates XML configuration. + + + +## Externalized Configuration + +Externalized Configuration is a core feature of Spring Boot , Dubbo Spring Boot not only supports it definitely , but also inherits Dubbo's Externalized Configuration, thus it provides single and multiple Dubbo's `*Config` Bindings from `PropertySources` , and `"dubbo."` is a common prefix of property name. + +> If you'd like to learn more , please read [Dubbo Externalized Configuration](http://dubbo.apache.org/zh-cn/blog/dubbo-externalized-configuration.html)(Chinese). + + + +### Single Dubbo Config Bean Bindings + +In most use scenarios , "Single Dubbo Config Bean Bindings" is enough , because a Dubbo application only requires single Bean of `*Config` (e.g `ApplicationConfig`). You add properties in `application.properties` to configure Dubbo's `*Config` Beans that you want , be like this : + +```properties +dubbo.application.name = foo +dubbo.application.owner = bar +dubbo.registry.address = 10.20.153.10:9090 +``` + +There are two Spring Beans will be initialized when Spring `ApplicatonContext` is ready, their Bean types are `ApplicationConfig` and `RegistryConfig`. + + + +#### Getting Single Dubbo Config Bean + + If application requires current `ApplicationConfig` Bean in somewhere , you can get it from Spring `BeanFactory` as those code : + +```java +BeanFactory beanFactory = .... +ApplicationConfig applicationConfig = beanFactory.getBean(ApplicationConfig.class) +``` + +or inject it : + +```java +@Autowired +private ApplicationConfig application; +``` + + + +#### Identifying Single Dubbo Config Bean + +If you'd like to identify this `ApplicationConfig` Bean , you could add **"id"** property: + +```properties +dubbo.application.id = application-bean-id +``` + + + +#### Mapping Single Dubbo Config Bean + +The whole Properties Mapping of "Single Dubbo Config Bean Bindings" lists below : + +| Dubbo `*Config` Type | The prefix of property name for Single Bindings | +| -------------------- | ---------------------------------------- | +| `ProtocolConfig` | `dubbo.protocol` | +| `ApplicationConfig` | `dubbo.application` | +| `ModuleConfig` | `dubbo.module` | +| `RegistryConfig` | `dubbo.registry` | +| `MonitorConfig` | `dubbo.monitor` | +| `ProviderConfig` | `dubbo.provider` | +| `ConsumerConfig` | `dubbo.consumer` | + + + +An example properties : + +```properties +# Single Dubbo Config Bindings +## ApplicationConfig +dubbo.application.id = applicationBean +dubbo.application.name = dubbo-demo-application + +## ModuleConfig +dubbo.module.id = moduleBean +dubbo.module.name = dubbo-demo-module + +## RegistryConfig +dubbo.registry.address = zookeeper://192.168.99.100:32770 + +## ProtocolConfig +dubbo.protocol.name = dubbo +dubbo.protocol.port = 20880 + +## MonitorConfig +dubbo.monitor.address = zookeeper://127.0.0.1:32770 + +## ProviderConfig +dubbo.provider.host = 127.0.0.1 + +## ConsumerConfig +dubbo.consumer.client = netty +``` + + + +### Multiple Dubbo Config Bean Bindings + +In contrast , "Multiple Dubbo Config Bean Bindings" means Externalized Configuration will be used to configure multiple Dubbo `*Config` Beans. + + + +#### Getting Multiple Dubbo Config Bean + +The whole Properties Mapping of "Multiple Dubbo Config Bean Bindings" lists below : + +| Dubbo `*Config` Type | The prefix of property name for Multiple Bindings | +| -------------------- | ---------------------------------------- | +| `ProtocolConfig` | `dubbo.protocols` | +| `ApplicationConfig` | `dubbo.applications` | +| `ModuleConfig` | `dubbo.modules` | +| `RegistryConfig` | `dubbo.registries` | +| `MonitorConfig` | `dubbo.monitors` | +| `ProviderConfig` | `dubbo.providers` | +| `ConsumerConfig` | `dubbo.consumers` | + + + +#### Identifying Multiple Dubbo Config Bean + +There is a different way to identify Multiple Dubbo Config Bean , the configuration pattern is like this : + +`${config-property-prefix}.${config-bean-id}.${property-name} = some value` , let's explain those placehoders : + +- `${config-property-prefix}` : The The prefix of property name for Multiple Bindings , e.g. `dubbo.protocols`, `dubbo.applications` and so on. +- `${config-bean-id}` : The bean id of Dubbo's `*Config` +- `${property-name}`: The property name of `*Config` + +An example properties : + +```properties +dubbo.applications.application1.name = dubbo-demo-application +dubbo.applications.application2.name = dubbo-demo-application2 +dubbo.modules.module1.name = dubbo-demo-module +dubbo.registries.registry1.address = zookeeper://192.168.99.100:32770 +dubbo.protocols.protocol1.name = dubbo +dubbo.protocols.protocol1.port = 20880 +dubbo.monitors.monitor1.address = zookeeper://127.0.0.1:32770 +dubbo.providers.provider1.host = 127.0.0.1 +dubbo.consumers.consumer1.client = netty +``` + + + +### IDE Support + + + +If you used advanced IDE tools , for instance [Jetbrains IDEA Ultimate](https://www.jetbrains.com/idea/) develops Dubbo Spring Boot application, it will popup the tips of Dubbo Configuration Bindings in `application.properties` : + + + +#### Case 1 - Single Bindings + +![](config-popup-window.png) + + + +#### Case 2 - Mutiple Bindings + +![](mconfig-popup-window.png) + +​ + diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/config-popup-window.png b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/config-popup-window.png new file mode 100644 index 00000000000..45246fd5314 Binary files /dev/null and b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/config-popup-window.png differ diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/mconfig-popup-window.png b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/mconfig-popup-window.png new file mode 100644 index 00000000000..fd8a7588ce5 Binary files /dev/null and b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/mconfig-popup-window.png differ diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml new file mode 100644 index 00000000000..98e68166246 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml @@ -0,0 +1,85 @@ + + + + + org.apache.dubbo + dubbo-spring-boot + ${revision} + ../pom.xml + + 4.0.0 + + dubbo-spring-boot-autoconfigure + jar + Apache Dubbo Spring Boot Auto-Configure + + + + + + + org.apache.dubbo + dubbo-spring-boot-autoconfigure-compatible + ${revision} + + + + + org.springframework.boot + spring-boot-autoconfigure + true + + + + org.springframework.boot + spring-boot-starter-logging + true + + + + + org.apache.dubbo + dubbo + ${revision} + + + + org.apache.dubbo + dubbo-common + ${revision} + true + + + + org.apache.dubbo + dubbo-config-spring + ${revision} + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinder.java b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinder.java new file mode 100644 index 00000000000..16e2dde9773 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinder.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.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.spring.context.properties.DubboConfigBinder; + +import com.alibaba.spring.context.config.ConfigurationBeanBinder; +import org.springframework.boot.context.properties.bind.BindHandler; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver; +import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBindHandler; +import org.springframework.boot.context.properties.bind.handler.NoUnboundElementsBindHandler; +import org.springframework.boot.context.properties.source.ConfigurationPropertySource; +import org.springframework.boot.context.properties.source.UnboundElementsSourceFilter; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +import java.util.Map; + +import static java.util.Arrays.asList; +import static org.springframework.boot.context.properties.source.ConfigurationPropertySources.from; + +/** + * Spring Boot Relaxed {@link DubboConfigBinder} implementation + * see org.springframework.boot.context.properties.ConfigurationPropertiesBinder + * + * @since 2.7.0 + */ +class BinderDubboConfigBinder implements ConfigurationBeanBinder { + + @Override + public void bind(Map configurationProperties, boolean ignoreUnknownFields, + boolean ignoreInvalidFields, Object configurationBean) { + + Iterable> propertySources = asList(new MapPropertySource("internal", configurationProperties)); + + // Converts ConfigurationPropertySources + Iterable configurationPropertySources = from(propertySources); + + // Wrap Bindable from DubboConfig instance + Bindable bindable = Bindable.ofInstance(configurationBean); + + Binder binder = new Binder(configurationPropertySources, new PropertySourcesPlaceholdersResolver(propertySources)); + + // Get BindHandler + BindHandler bindHandler = getBindHandler(ignoreUnknownFields, ignoreInvalidFields); + + // Bind + binder.bind("", bindable, bindHandler); + } + + private BindHandler getBindHandler(boolean ignoreUnknownFields, + boolean ignoreInvalidFields) { + BindHandler handler = BindHandler.DEFAULT; + if (ignoreInvalidFields) { + handler = new IgnoreErrorsBindHandler(handler); + } + if (!ignoreUnknownFields) { + UnboundElementsSourceFilter filter = new UnboundElementsSourceFilter(); + handler = new NoUnboundElementsBindHandler(handler, filter); + } + return handler; + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfiguration.java new file mode 100644 index 00000000000..d82747d163c --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfiguration.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.dubbo.spring.boot.autoconfigure; + +import com.alibaba.spring.context.config.ConfigurationBeanBinder; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertyResolver; + +import java.util.Map; +import java.util.Set; + +import static com.alibaba.spring.util.PropertySourcesUtils.getSubProperties; +import static java.util.Collections.emptySet; +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_BEAN_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_PROPERTY_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SCAN_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME; +import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE; + +/** + * Dubbo Relaxed Binding Auto-{@link Configuration} for Spring Boot 2.0 + * + * @see DubboRelaxedBindingAutoConfiguration + * @since 2.7.0 + */ +@Configuration +@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true) +@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder") +@AutoConfigureBefore(DubboRelaxedBindingAutoConfiguration.class) +public class DubboRelaxedBinding2AutoConfiguration { + + public PropertyResolver dubboScanBasePackagesPropertyResolver(ConfigurableEnvironment environment) { + ConfigurableEnvironment propertyResolver = new AbstractEnvironment() { + @Override + protected void customizePropertySources(MutablePropertySources propertySources) { + Map dubboScanProperties = getSubProperties(environment.getPropertySources(), DUBBO_SCAN_PREFIX); + propertySources.addLast(new MapPropertySource("dubboScanProperties", dubboScanProperties)); + } + }; + ConfigurationPropertySources.attach(propertyResolver); + return propertyResolver; + } + + /** + * The bean is used to scan the packages of Dubbo Service classes + * + * @param environment {@link Environment} instance + * @return non-null {@link Set} + * @since 2.7.8 + */ + @ConditionalOnMissingBean(name = BASE_PACKAGES_BEAN_NAME) + @Bean(name = BASE_PACKAGES_BEAN_NAME) + public Set dubboBasePackages(ConfigurableEnvironment environment) { + PropertyResolver propertyResolver = dubboScanBasePackagesPropertyResolver(environment); + return propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet()); + } + + @ConditionalOnMissingBean(name = RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME, value = ConfigurationBeanBinder.class) + @Bean(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME) + @Scope(scopeName = SCOPE_PROTOTYPE) + public ConfigurationBeanBinder relaxedDubboConfigBinder() { + return new BinderDubboConfigBinder(); + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000000..057eb454158 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBinding2AutoConfiguration \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinderTest.java b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinderTest.java new file mode 100644 index 00000000000..45b8c336f01 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinderTest.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.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.RegistryConfig; + +import com.alibaba.spring.context.config.ConfigurationBeanBinder; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Map; + +import static com.alibaba.spring.util.PropertySourcesUtils.getSubProperties; + +/** + * {@link BinderDubboConfigBinder} Test + * + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@TestPropertySource(locations = "classpath:/dubbo.properties") +@ContextConfiguration(classes = BinderDubboConfigBinder.class) +public class BinderDubboConfigBinderTest { + + @Autowired + private ConfigurationBeanBinder dubboConfigBinder; + + @Autowired + private ConfigurableEnvironment environment; + + @Test + public void testBinder() { + + ApplicationConfig applicationConfig = new ApplicationConfig(); + Map properties = getSubProperties(environment.getPropertySources(), "dubbo.application"); + dubboConfigBinder.bind(properties, true, true, applicationConfig); + Assert.assertEquals("hello", applicationConfig.getName()); + Assert.assertEquals("world", applicationConfig.getOwner()); + + RegistryConfig registryConfig = new RegistryConfig(); + properties = getSubProperties(environment.getPropertySources(), "dubbo.registry"); + dubboConfigBinder.bind(properties, true, true, registryConfig); + Assert.assertEquals("10.20.153.17", registryConfig.getAddress()); + + ProtocolConfig protocolConfig = new ProtocolConfig(); + properties = getSubProperties(environment.getPropertySources(), "dubbo.protocol"); + dubboConfigBinder.bind(properties, true, true, protocolConfig); + Assert.assertEquals(Integer.valueOf(20881), protocolConfig.getPort()); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfigurationTest.java b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfigurationTest.java new file mode 100644 index 00000000000..adc2f33d049 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfigurationTest.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.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; +import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor; + +import com.alibaba.spring.context.config.ConfigurationBeanBinder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.ClassUtils; + +import java.util.Map; +import java.util.Set; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_BEAN_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * {@link DubboRelaxedBinding2AutoConfiguration} Test + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = DubboRelaxedBinding2AutoConfigurationTest.class, properties = { + "dubbo.scan.basePackages = org.apache.dubbo.spring.boot.autoconfigure" +}) +@EnableAutoConfiguration +@PropertySource(value = "classpath:/dubbo.properties") +public class DubboRelaxedBinding2AutoConfigurationTest { + + @Autowired + @Qualifier(BASE_PACKAGES_BEAN_NAME) + private Set packagesToScan; + + @Autowired + @Qualifier(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME) + private ConfigurationBeanBinder dubboConfigBinder; + + @Autowired + private ObjectProvider serviceClassPostProcessor; + + @Autowired + private ObjectProvider referenceAnnotationBeanPostProcessor; + + @Autowired + private Environment environment; + + @Autowired + private Map environments; + + @Test + public void testBeans() { + + assertTrue(ClassUtils.isAssignableValue(BinderDubboConfigBinder.class, dubboConfigBinder)); + + assertNotNull(serviceClassPostProcessor); + assertNotNull(serviceClassPostProcessor.getIfAvailable()); + assertNotNull(referenceAnnotationBeanPostProcessor); + assertNotNull(referenceAnnotationBeanPostProcessor.getIfAvailable()); + + assertNotNull(environment); + assertNotNull(environments); + + + assertEquals(1, environments.size()); + + assertTrue(environments.containsValue(environment)); + } + +} \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java new file mode 100644 index 00000000000..860bc2e8a34 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.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.dubbo.spring.boot.env; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.core.Ordered; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.mock.env.MockEnvironment; + +import java.util.HashMap; + +/** + * {@link DubboDefaultPropertiesEnvironmentPostProcessor} Test + */ +public class DubboDefaultPropertiesEnvironmentPostProcessorTest { + + private DubboDefaultPropertiesEnvironmentPostProcessor instance = + new DubboDefaultPropertiesEnvironmentPostProcessor(); + + private SpringApplication springApplication = new SpringApplication(); + + @Test + public void testOrder() { + Assert.assertEquals(Ordered.LOWEST_PRECEDENCE, instance.getOrder()); + } + + @Test + public void testPostProcessEnvironment() { + MockEnvironment environment = new MockEnvironment(); + // Case 1 : Not Any property + instance.postProcessEnvironment(environment, springApplication); + // Get PropertySources + MutablePropertySources propertySources = environment.getPropertySources(); + // Nothing to change + PropertySource defaultPropertySource = propertySources.get("defaultProperties"); + Assert.assertNotNull(defaultPropertySource); + Assert.assertEquals("true", defaultPropertySource.getProperty("dubbo.config.multiple")); + Assert.assertEquals("false", defaultPropertySource.getProperty("dubbo.application.qos-enable")); + + // Case 2 : Only set property "spring.application.name" + environment.setProperty("spring.application.name", "demo-dubbo-application"); + instance.postProcessEnvironment(environment, springApplication); + defaultPropertySource = propertySources.get("defaultProperties"); + Object dubboApplicationName = defaultPropertySource.getProperty("dubbo.application.name"); + Assert.assertEquals("demo-dubbo-application", dubboApplicationName); + + // Case 3 : Only set property "dubbo.application.name" + // Rest environment + environment = new MockEnvironment(); + propertySources = environment.getPropertySources(); + environment.setProperty("dubbo.application.name", "demo-dubbo-application"); + instance.postProcessEnvironment(environment, springApplication); + defaultPropertySource = propertySources.get("defaultProperties"); + Assert.assertNotNull(defaultPropertySource); + dubboApplicationName = environment.getProperty("dubbo.application.name"); + Assert.assertEquals("demo-dubbo-application", dubboApplicationName); + + // Case 4 : If "defaultProperties" PropertySource is present in PropertySources + // Rest environment + environment = new MockEnvironment(); + propertySources = environment.getPropertySources(); + propertySources.addLast(new MapPropertySource("defaultProperties", new HashMap())); + environment.setProperty("spring.application.name", "demo-dubbo-application"); + instance.postProcessEnvironment(environment, springApplication); + defaultPropertySource = propertySources.get("defaultProperties"); + dubboApplicationName = defaultPropertySource.getProperty("dubbo.application.name"); + Assert.assertEquals("demo-dubbo-application", dubboApplicationName); + + // Case 5 : Rest dubbo.config.multiple and dubbo.application.qos-enable + environment = new MockEnvironment(); + propertySources = environment.getPropertySources(); + propertySources.addLast(new MapPropertySource("defaultProperties", new HashMap())); + environment.setProperty("dubbo.config.multiple", "false"); + environment.setProperty("dubbo.application.qos-enable", "true"); + instance.postProcessEnvironment(environment, springApplication); + Assert.assertEquals("false", environment.getProperty("dubbo.config.multiple")); + Assert.assertEquals("true", environment.getProperty("dubbo.application.qos-enable")); + } +} \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.java b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.java new file mode 100644 index 00000000000..b70f35145cd --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.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.dubbo.spring.boot.util; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.StandardEnvironment; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@link EnvironmentUtils} Test + * + * @see EnvironmentUtils + * @since 2.7.0 + */ +public class EnvironmentUtilsTest { + + @Test + public void testExtraProperties() { + + System.setProperty("user.name", "mercyblitz"); + + StandardEnvironment environment = new StandardEnvironment(); + + Map map = new HashMap<>(); + + map.put("user.name", "Mercy"); + + MapPropertySource propertySource = new MapPropertySource("first", map); + + CompositePropertySource compositePropertySource = new CompositePropertySource("comp"); + + compositePropertySource.addFirstPropertySource(propertySource); + + MutablePropertySources propertySources = environment.getPropertySources(); + + propertySources.addFirst(compositePropertySource); + + Map properties = EnvironmentUtils.extractProperties(environment); + + Assert.assertEquals("Mercy", properties.get("user.name")); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/resources/dubbo.properties b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/resources/dubbo.properties new file mode 100644 index 00000000000..abe14a103da --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/resources/dubbo.properties @@ -0,0 +1,5 @@ +dubbo.application.NAME=hello +dubbo.application.owneR=world +dubbo.registry.Address=10.20.153.17 +dubbo.protocol.pORt=20881 +dubbo.service.invoke.timeout=2000 \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/pom.xml new file mode 100644 index 00000000000..2949cd7a13a --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/pom.xml @@ -0,0 +1,94 @@ + + + + + org.apache.dubbo + dubbo-spring-boot-compatible + ${revision} + ../pom.xml + + 4.0.0 + + dubbo-spring-boot-actuator-compatible + Apache Dubbo Spring Boot Compatible for Spring Boot 1.x Actuator + + + + + org.springframework.boot + spring-boot-starter-web + true + + + + org.springframework.boot + spring-boot-starter-actuator + true + + + + org.springframework.boot + spring-boot-autoconfigure + true + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.apache.dubbo + dubbo-spring-boot-autoconfigure-compatible + ${revision} + + + + org.apache.dubbo + dubbo + ${revision} + + + + org.apache.dubbo + dubbo-common + ${revision} + true + + + + org.apache.dubbo + dubbo-config-spring + ${revision} + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfiguration.java new file mode 100644 index 00000000000..84091242bfb --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfiguration.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.dubbo.spring.boot.actuate.autoconfigure; + + +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboEndpoint; +import org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration; +import org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration; + +import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint; +import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Dubbo {@link Endpoint} Auto Configuration is compatible with Spring Boot Actuator 1.x + * + * @since 2.7.0 + */ +@Configuration +@ConditionalOnClass(name = { + "org.springframework.boot.actuate.endpoint.Endpoint" // Spring Boot 1.x +}) +@AutoConfigureAfter(value = { + DubboAutoConfiguration.class, + DubboRelaxedBindingAutoConfiguration.class +}) +@EnableConfigurationProperties(DubboEndpoint.class) +public class DubboEndpointAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnEnabledEndpoint(value = "dubbo") + public DubboEndpoint dubboEndpoint() { + return new DubboEndpoint(); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java new file mode 100644 index 00000000000..e6dd0a4d16e --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.autoconfigure; + +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.AbstractDubboMetadata; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * Dubbo Endpoints Metadata Auto-{@link Configuration} + */ +@ConditionalOnClass(name = { + "org.springframework.boot.actuate.health.Health" // If spring-boot-actuator is present +}) +@Configuration +@AutoConfigureAfter(name = { + "org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration", + "org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration" +}) +@ComponentScan(basePackageClasses = AbstractDubboMetadata.class) +public class DubboEndpointMetadataAutoConfiguration { +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java new file mode 100644 index 00000000000..1bcfcb0b496 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.autoconfigure; + +import org.apache.dubbo.spring.boot.actuate.health.DubboHealthIndicator; +import org.apache.dubbo.spring.boot.actuate.health.DubboHealthIndicatorProperties; + +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Dubbo {@link DubboHealthIndicator} Auto Configuration + * + * @see HealthIndicator + * @since 2.7.0 + */ +@Configuration +@ConditionalOnClass(name = { + "org.springframework.boot.actuate.health.Health" +}) +@ConditionalOnProperty(name = "management.health.dubbo.enabled", matchIfMissing = true, havingValue = "true") +@EnableConfigurationProperties(DubboHealthIndicatorProperties.class) +public class DubboHealthIndicatorAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public DubboHealthIndicator dubboHealthIndicator() { + return new DubboHealthIndicator(); + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMvcEndpointManagementContextConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMvcEndpointManagementContextConfiguration.java new file mode 100644 index 00000000000..5b5991876f1 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMvcEndpointManagementContextConfiguration.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.autoconfigure; + + +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.mvc.DubboMvcEndpoint; + +import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; + +/** + * Dubbo {@link MvcEndpoint} {@link ManagementContextConfiguration} for Spring Boot 1.x + * + * @since 2.7.0 + */ +@ManagementContextConfiguration +@ConditionalOnClass(name = { + "org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter" +}) +@ConditionalOnWebApplication +public class DubboMvcEndpointManagementContextConfiguration { + + @Bean + @ConditionalOnBean(DubboEndpoint.class) + @ConditionalOnMissingBean + public DubboMvcEndpoint dubboMvcEndpoint(DubboEndpoint dubboEndpoint) { + return new DubboMvcEndpoint(dubboEndpoint); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpoint.java new file mode 100644 index 00000000000..ef0d9b9565c --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpoint.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint; + + +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboMetadata; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.AbstractEndpoint; +import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Map; + +/** + * Actuator {@link Endpoint} to expose Dubbo Meta Data + * + * @see Endpoint + * @since 2.7.0 + */ +@ConfigurationProperties(prefix = "endpoints.dubbo", ignoreUnknownFields = false) +public class DubboEndpoint extends AbstractEndpoint> { + + @Autowired + private DubboMetadata dubboMetadata; + + public DubboEndpoint() { + super("dubbo", true, false); + } + + @Override + public Map invoke() { + return dubboMetadata.invoke(); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/AbstractDubboMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/AbstractDubboMetadata.java new file mode 100644 index 00000000000..27861a6d09e --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/AbstractDubboMetadata.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint.metadata; + +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.spring.ServiceBean; +import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.BEAN_NAME; +import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors; +import static org.springframework.util.ClassUtils.isPrimitiveOrWrapper; + +/** + * Abstract Dubbo Metadata + * + * @since 2.7.0 + */ +public abstract class AbstractDubboMetadata implements ApplicationContextAware, EnvironmentAware { + + protected ApplicationContext applicationContext; + + protected ConfigurableEnvironment environment; + + private static boolean isSimpleType(Class type) { + return isPrimitiveOrWrapper(type) + || type == String.class + || type == BigDecimal.class + || type == BigInteger.class + || type == Date.class + || type == URL.class + || type == Class.class + ; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void setEnvironment(Environment environment) { + if (environment instanceof ConfigurableEnvironment) { + this.environment = (ConfigurableEnvironment) environment; + } + } + + protected Map resolveBeanMetadata(final Object bean) { + + final Map beanMetadata = new LinkedHashMap<>(); + + try { + + BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + + Method readMethod = propertyDescriptor.getReadMethod(); + + if (readMethod != null && isSimpleType(propertyDescriptor.getPropertyType())) { + + String name = Introspector.decapitalize(propertyDescriptor.getName()); + Object value = readMethod.invoke(bean); + + beanMetadata.put(name, value); + } + + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + + return beanMetadata; + + } + + protected Map getServiceBeansMap() { + return beansOfTypeIncludingAncestors(applicationContext, ServiceBean.class); + } + + protected ReferenceAnnotationBeanPostProcessor getReferenceAnnotationBeanPostProcessor() { + return applicationContext.getBean(BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class); + } + + protected Map getProtocolConfigsBeanMap() { + return beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class); + } + + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboConfigsMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboConfigsMetadata.java new file mode 100644 index 00000000000..081c2c1a729 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboConfigsMetadata.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.dubbo.spring.boot.actuate.endpoint.metadata; + +import org.apache.dubbo.config.AbstractConfig; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ConsumerConfig; +import org.apache.dubbo.config.MethodConfig; +import org.apache.dubbo.config.ModuleConfig; +import org.apache.dubbo.config.MonitorConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ProviderConfig; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ServiceConfig; + +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; + +import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors; + +/** + * Dubbo Configs Metadata + * + * @since 2.7.0 + */ +@Component +public class DubboConfigsMetadata extends AbstractDubboMetadata { + + public Map>> configs() { + + Map>> configsMap = new LinkedHashMap<>(); + + addDubboConfigBeans(ApplicationConfig.class, configsMap); + addDubboConfigBeans(ConsumerConfig.class, configsMap); + addDubboConfigBeans(MethodConfig.class, configsMap); + addDubboConfigBeans(ModuleConfig.class, configsMap); + addDubboConfigBeans(MonitorConfig.class, configsMap); + addDubboConfigBeans(ProtocolConfig.class, configsMap); + addDubboConfigBeans(ProviderConfig.class, configsMap); + addDubboConfigBeans(ReferenceConfig.class, configsMap); + addDubboConfigBeans(RegistryConfig.class, configsMap); + addDubboConfigBeans(ServiceConfig.class, configsMap); + + return configsMap; + + } + + private void addDubboConfigBeans(Class dubboConfigClass, + Map>> configsMap) { + + Map dubboConfigBeans = beansOfTypeIncludingAncestors(applicationContext, dubboConfigClass); + + String name = dubboConfigClass.getSimpleName(); + + Map> beansMetadata = new TreeMap<>(); + + for (Map.Entry entry : dubboConfigBeans.entrySet()) { + + String beanName = entry.getKey(); + AbstractConfig configBean = entry.getValue(); + Map configBeanMeta = resolveBeanMetadata(configBean); + beansMetadata.put(beanName, configBeanMeta); + + } + + configsMap.put(name, beansMetadata); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboMetadata.java new file mode 100644 index 00000000000..9149ca7f9d7 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboMetadata.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint.metadata; + +import org.apache.dubbo.common.Version; +import org.apache.dubbo.spring.boot.util.DubboUtils; + +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_GITHUB_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_MAILING_LIST; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SPRING_BOOT_GITHUB_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SPRING_BOOT_GIT_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SPRING_BOOT_ISSUES_URL; + +/** + * Dubbo Metadata + * @since 2.7.0 + */ +@Component +public class DubboMetadata { + + public Map invoke() { + + Map metaData = new LinkedHashMap<>(); + + metaData.put("timestamp", System.currentTimeMillis()); + + Map versions = new LinkedHashMap<>(); + versions.put("dubbo-spring-boot", Version.getVersion(DubboUtils.class, "1.0.0")); + versions.put("dubbo", Version.getVersion()); + + Map urls = new LinkedHashMap<>(); + urls.put("dubbo", DUBBO_GITHUB_URL); + urls.put("mailing-list", DUBBO_MAILING_LIST); + urls.put("github", DUBBO_SPRING_BOOT_GITHUB_URL); + urls.put("issues", DUBBO_SPRING_BOOT_ISSUES_URL); + urls.put("git", DUBBO_SPRING_BOOT_GIT_URL); + + metaData.put("versions", versions); + metaData.put("urls", urls); + + return metaData; + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboPropertiesMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboPropertiesMetadata.java new file mode 100644 index 00000000000..f7edc04af15 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboPropertiesMetadata.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.dubbo.spring.boot.actuate.endpoint.metadata; + +import org.springframework.stereotype.Component; + +import java.util.SortedMap; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.filterDubboProperties; + +/** + * Dubbo Properties Metadata + * + * @since 2.7.0 + */ +@Component +public class DubboPropertiesMetadata extends AbstractDubboMetadata { + + public SortedMap properties() { + return filterDubboProperties(environment); + } +} \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboReferencesMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboReferencesMetadata.java new file mode 100644 index 00000000000..af464000c95 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboReferencesMetadata.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.dubbo.spring.boot.actuate.endpoint.metadata; + +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.dubbo.config.spring.ReferenceBean; +import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; + +import org.springframework.beans.factory.annotation.InjectionMetadata; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * {@link DubboReference} Metadata + * + * @since 2.7.0 + */ +@Component +public class DubboReferencesMetadata extends AbstractDubboMetadata { + + public Map> references() { + + Map> referencesMetadata = new LinkedHashMap<>(); + + ReferenceAnnotationBeanPostProcessor beanPostProcessor = getReferenceAnnotationBeanPostProcessor(); + + referencesMetadata.putAll(buildReferencesMetadata(beanPostProcessor.getInjectedFieldReferenceBeanMap())); + referencesMetadata.putAll(buildReferencesMetadata(beanPostProcessor.getInjectedMethodReferenceBeanMap())); + + return referencesMetadata; + + } + + private Map> buildReferencesMetadata( + Map> injectedElementReferenceBeanMap) { + Map> referencesMetadata = new LinkedHashMap<>(); + + for (Map.Entry> entry : + injectedElementReferenceBeanMap.entrySet()) { + + InjectionMetadata.InjectedElement injectedElement = entry.getKey(); + + ReferenceBean referenceBean = entry.getValue(); + + Map beanMetadata = resolveBeanMetadata(referenceBean); + beanMetadata.put("invoker", resolveBeanMetadata(referenceBean.get())); + + referencesMetadata.put(String.valueOf(injectedElement.getMember()), beanMetadata); + + } + + return referencesMetadata; + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboServicesMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboServicesMetadata.java new file mode 100644 index 00000000000..0053e2be391 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboServicesMetadata.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.endpoint.metadata; + +import org.apache.dubbo.config.annotation.DubboService; +import org.apache.dubbo.config.spring.ServiceBean; + +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * {@link DubboService} Metadata + * + * @since 2.7.0 + */ +@Component +public class DubboServicesMetadata extends AbstractDubboMetadata { + + public Map> services() { + + Map serviceBeansMap = getServiceBeansMap(); + + Map> servicesMetadata = new LinkedHashMap<>(serviceBeansMap.size()); + + for (Map.Entry entry : serviceBeansMap.entrySet()) { + + String serviceBeanName = entry.getKey(); + + ServiceBean serviceBean = entry.getValue(); + + Map serviceBeanMetadata = resolveBeanMetadata(serviceBean); + + Object service = resolveServiceBean(serviceBeanName, serviceBean); + + if (service != null) { + // Add Service implementation class + serviceBeanMetadata.put("serviceClass", service.getClass().getName()); + } + + servicesMetadata.put(serviceBeanName, serviceBeanMetadata); + + } + + return servicesMetadata; + + } + + private Object resolveServiceBean(String serviceBeanName, ServiceBean serviceBean) { + + int index = serviceBeanName.indexOf("#"); + + if (index > -1) { + + Class interfaceClass = serviceBean.getInterfaceClass(); + + String serviceName = serviceBeanName.substring(index + 1); + + if (applicationContext.containsBean(serviceName)) { + return applicationContext.getBean(serviceName, interfaceClass); + } + + } + + return null; + + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java new file mode 100644 index 00000000000..d14c7f17db7 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.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.dubbo.spring.boot.actuate.endpoint.metadata; + +import org.apache.dubbo.config.spring.ServiceBean; +import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; + +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; + +import static org.apache.dubbo.registry.support.AbstractRegistryFactory.getRegistries; + +/** + * Dubbo Shutdown + * + * @since 2.7.0 + */ +@Component +public class DubboShutdownMetadata extends AbstractDubboMetadata { + + + public Map shutdown() throws Exception { + + Map shutdownCountData = new LinkedHashMap<>(); + + // registries + int registriesCount = getRegistries().size(); + + // protocols + int protocolsCount = getProtocolConfigsBeanMap().size(); + + shutdownCountData.put("registries", registriesCount); + shutdownCountData.put("protocols", protocolsCount); + + // Service Beans + Map serviceBeansMap = getServiceBeansMap(); + if (!serviceBeansMap.isEmpty()) { + for (ServiceBean serviceBean : serviceBeansMap.values()) { + serviceBean.destroy(); + } + } + shutdownCountData.put("services", serviceBeansMap.size()); + + // Reference Beans + ReferenceAnnotationBeanPostProcessor beanPostProcessor = getReferenceAnnotationBeanPostProcessor(); + + int referencesCount = beanPostProcessor.getReferenceBeans().size(); + + beanPostProcessor.destroy(); + + shutdownCountData.put("references", referencesCount); + + // Set Result to complete + Map shutdownData = new TreeMap<>(); + shutdownData.put("shutdown.count", shutdownCountData); + + + return shutdownData; + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/mvc/DubboMvcEndpoint.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/mvc/DubboMvcEndpoint.java new file mode 100644 index 00000000000..ac3bd1e7cf5 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/mvc/DubboMvcEndpoint.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.dubbo.spring.boot.actuate.endpoint.mvc; + +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboConfigsMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboPropertiesMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboReferencesMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboServicesMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboShutdownMetadata; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; +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.ResponseBody; +import org.springframework.web.context.request.async.DeferredResult; + +import java.util.Map; +import java.util.SortedMap; + +/** + * {@link MvcEndpoint} to expose Dubbo Metadata + * + * @see MvcEndpoint + * @since 2.7.0 + */ +public class DubboMvcEndpoint extends EndpointMvcAdapter { + + public static final String DUBBO_SHUTDOWN_ENDPOINT_URI = "/shutdown"; + + public static final String DUBBO_CONFIGS_ENDPOINT_URI = "/configs"; + + public static final String DUBBO_SERVICES_ENDPOINT_URI = "/services"; + + public static final String DUBBO_REFERENCES_ENDPOINT_URI = "/references"; + + public static final String DUBBO_PROPERTIES_ENDPOINT_URI = "/properties"; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private DubboShutdownMetadata dubboShutdownMetadata; + + @Autowired + private DubboConfigsMetadata dubboConfigsMetadata; + + @Autowired + private DubboServicesMetadata dubboServicesMetadata; + + @Autowired + private DubboReferencesMetadata dubboReferencesMetadata; + + @Autowired + private DubboPropertiesMetadata dubboPropertiesMetadata; + + public DubboMvcEndpoint(DubboEndpoint dubboEndpoint) { + super(dubboEndpoint); + } + + + @RequestMapping(value = DUBBO_SHUTDOWN_ENDPOINT_URI, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public DeferredResult shutdown() throws Exception { + Map shutdownCountData = dubboShutdownMetadata.shutdown(); + return new DeferredResult(null, shutdownCountData); + } + + @RequestMapping(value = DUBBO_CONFIGS_ENDPOINT_URI, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public Map>> configs() { + return dubboConfigsMetadata.configs(); + } + + + @RequestMapping(value = DUBBO_SERVICES_ENDPOINT_URI, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public Map> services() { + return dubboServicesMetadata.services(); + } + + @RequestMapping(value = DUBBO_REFERENCES_ENDPOINT_URI, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public Map> references() { + return dubboReferencesMetadata.references(); + } + + @RequestMapping(value = DUBBO_PROPERTIES_ENDPOINT_URI, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public SortedMap properties() { + return dubboPropertiesMetadata.properties(); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java new file mode 100644 index 00000000000..87d0a0fa488 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.health; + +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.status.StatusChecker; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ProviderConfig; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.AbstractHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.util.StringUtils; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader; + +/** + * Dubbo {@link HealthIndicator} + * + * @see HealthIndicator + * @since 2.7.0 + */ +public class DubboHealthIndicator extends AbstractHealthIndicator { + + @Autowired + private DubboHealthIndicatorProperties dubboHealthIndicatorProperties; + + @Autowired(required = false) + private Map protocolConfigs = Collections.emptyMap(); + + @Autowired(required = false) + private Map providerConfigs = Collections.emptyMap(); + + @Override + protected void doHealthCheck(Health.Builder builder) throws Exception { + + ExtensionLoader extensionLoader = getExtensionLoader(StatusChecker.class); + + Map statusCheckerNamesMap = resolveStatusCheckerNamesMap(); + + boolean hasError = false; + + boolean hasUnknown = false; + + // Up first + builder.up(); + + for (Map.Entry entry : statusCheckerNamesMap.entrySet()) { + + String statusCheckerName = entry.getKey(); + + String source = entry.getValue(); + + StatusChecker checker = extensionLoader.getExtension(statusCheckerName); + + org.apache.dubbo.common.status.Status status = checker.check(); + + org.apache.dubbo.common.status.Status.Level level = status.getLevel(); + + if (!hasError && level.equals(org.apache.dubbo.common.status.Status.Level.ERROR)) { + hasError = true; + builder.down(); + } + + if (!hasError && !hasUnknown && level.equals(org.apache.dubbo.common.status.Status.Level.UNKNOWN)) { + hasUnknown = true; + builder.unknown(); + } + + Map detail = new LinkedHashMap<>(); + + detail.put("source", source); + detail.put("status", status); + + builder.withDetail(statusCheckerName, detail); + + } + + + } + + /** + * Resolves the map of {@link StatusChecker}'s name and its' source. + * + * @return non-null {@link Map} + */ + protected Map resolveStatusCheckerNamesMap() { + + Map statusCheckerNamesMap = new LinkedHashMap<>(); + + statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties()); + + statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromProtocolConfigs()); + + statusCheckerNamesMap.putAll(resolveStatusCheckerNamesMapFromProviderConfig()); + + return statusCheckerNamesMap; + + } + + private Map resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties() { + + DubboHealthIndicatorProperties.Status status = + dubboHealthIndicatorProperties.getStatus(); + + Map statusCheckerNamesMap = new LinkedHashMap<>(); + + for (String statusName : status.getDefaults()) { + + statusCheckerNamesMap.put(statusName, DubboHealthIndicatorProperties.PREFIX + ".status.defaults"); + + } + + for (String statusName : status.getExtras()) { + + statusCheckerNamesMap.put(statusName, DubboHealthIndicatorProperties.PREFIX + ".status.extras"); + + } + + return statusCheckerNamesMap; + + } + + + private Map resolveStatusCheckerNamesMapFromProtocolConfigs() { + + Map statusCheckerNamesMap = new LinkedHashMap<>(); + + for (Map.Entry entry : protocolConfigs.entrySet()) { + + String beanName = entry.getKey(); + + ProtocolConfig protocolConfig = entry.getValue(); + + Set statusCheckerNames = getStatusCheckerNames(protocolConfig); + + for (String statusCheckerName : statusCheckerNames) { + + String source = buildSource(beanName, protocolConfig); + + statusCheckerNamesMap.put(statusCheckerName, source); + + } + + } + + return statusCheckerNamesMap; + + } + + private Map resolveStatusCheckerNamesMapFromProviderConfig() { + + Map statusCheckerNamesMap = new LinkedHashMap<>(); + + for (Map.Entry entry : providerConfigs.entrySet()) { + + String beanName = entry.getKey(); + + ProviderConfig providerConfig = entry.getValue(); + + Set statusCheckerNames = getStatusCheckerNames(providerConfig); + + for (String statusCheckerName : statusCheckerNames) { + + String source = buildSource(beanName, providerConfig); + + statusCheckerNamesMap.put(statusCheckerName, source); + + } + + } + + return statusCheckerNamesMap; + + } + + private Set getStatusCheckerNames(ProtocolConfig protocolConfig) { + String status = protocolConfig.getStatus(); + return StringUtils.commaDelimitedListToSet(status); + } + + private Set getStatusCheckerNames(ProviderConfig providerConfig) { + String status = providerConfig.getStatus(); + return StringUtils.commaDelimitedListToSet(status); + } + + private String buildSource(String beanName, Object bean) { + return beanName + "@" + bean.getClass().getSimpleName() + ".getStatus()"; + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java new file mode 100644 index 00000000000..7ce20659e69 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.health; + +import org.apache.dubbo.common.status.StatusChecker; + +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +import static org.apache.dubbo.spring.boot.actuate.health.DubboHealthIndicatorProperties.PREFIX; + +/** + * Dubbo {@link HealthIndicator} Properties + * + * @see HealthIndicator + * @since 2.7.0 + */ +@ConfigurationProperties(prefix = PREFIX, ignoreUnknownFields = false) +public class DubboHealthIndicatorProperties { + + /** + * The prefix of {@link DubboHealthIndicatorProperties} + */ + public static final String PREFIX = "management.health.dubbo"; + + private Status status = new Status(); + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + /** + * The nested class for {@link StatusChecker}'s names + *
    +     * registry= org.apache.dubbo.registry.status.RegistryStatusChecker
    +     * spring= org.apache.dubbo.config.spring.status.SpringStatusChecker
    +     * datasource= org.apache.dubbo.config.spring.status.DataSourceStatusChecker
    +     * memory= org.apache.dubbo.common.status.support.MemoryStatusChecker
    +     * load= org.apache.dubbo.common.status.support.LoadStatusChecker
    +     * server= org.apache.dubbo.rpc.protocol.dubbo.status.ServerStatusChecker
    +     * threadpool= org.apache.dubbo.rpc.protocol.dubbo.status.ThreadPoolStatusChecker
    +     * 
    + * + * @see StatusChecker + */ + public static class Status { + + /** + * The defaults names of {@link StatusChecker} + *

    + * The defaults : "memory", "load" + */ + private Set defaults = new LinkedHashSet<>(Arrays.asList("memory", "load")); + + /** + * The extra names of {@link StatusChecker} + */ + private Set extras = new LinkedHashSet<>(); + + public Set getDefaults() { + return defaults; + } + + public void setDefaults(Set defaults) { + this.defaults = defaults; + } + + public Set getExtras() { + return extras; + } + + public void setExtras(Set extras) { + this.extras = extras; + } + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/resources/META-INF/spring.factories b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000000..db1b811c3ec --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/resources/META-INF/spring.factories @@ -0,0 +1,6 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboEndpointAutoConfiguration,\ +org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboHealthIndicatorAutoConfiguration,\ +org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboEndpointMetadataAutoConfiguration +org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\ +org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboMvcEndpointManagementContextConfiguration \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfigurationTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfigurationTest.java new file mode 100644 index 00000000000..f383bc09b25 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfigurationTest.java @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.autoconfigure; + +import org.apache.dubbo.config.annotation.DubboService; +import org.apache.dubbo.spring.boot.actuate.endpoint.DubboEndpoint; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboConfigsMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboPropertiesMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboReferencesMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboServicesMetadata; +import org.apache.dubbo.spring.boot.actuate.endpoint.metadata.DubboShutdownMetadata; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; +import java.util.SortedMap; +import java.util.function.Supplier; + +/** + * {@link DubboEndpointAutoConfiguration} Test + * + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = { + DubboEndpointAutoConfiguration.class, + DubboEndpointAutoConfigurationTest.class + }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "dubbo.service.version = 1.0.0", + "dubbo.application.id = my-application", + "dubbo.application.name = dubbo-demo-application", + "dubbo.module.id = my-module", + "dubbo.module.name = dubbo-demo-module", + "dubbo.registry.id = my-registry", + "dubbo.registry.address = N/A", + "dubbo.protocol.id=my-protocol", + "dubbo.protocol.name=dubbo", + "dubbo.protocol.port=20880", + "dubbo.provider.id=my-provider", + "dubbo.provider.host=127.0.0.1", + "dubbo.scan.basePackages=org.apache.dubbo.spring.boot.actuate.autoconfigure", + "endpoints.enabled = true", + "management.security.enabled = false", + "management.contextPath = /actuator", + "endpoints.dubbo.enabled = true", + "endpoints.dubbo.sensitive = false", + "endpoints.dubboshutdown.enabled = true", + "endpoints.dubboconfigs.enabled = true", + "endpoints.dubboservices.enabled = true", + "endpoints.dubboreferences.enabled = true", + "endpoints.dubboproperties.enabled = true", + }) +@EnableAutoConfiguration +@Ignore +public class DubboEndpointAutoConfigurationTest { + + @Autowired + private DubboEndpoint dubboEndpoint; + + @Autowired + private DubboConfigsMetadata dubboConfigsMetadata; + + @Autowired + private DubboPropertiesMetadata dubboProperties; + + @Autowired + private DubboReferencesMetadata dubboReferencesMetadata; + + @Autowired + private DubboServicesMetadata dubboServicesMetadata; + + @Autowired + private DubboShutdownMetadata dubboShutdownMetadata; + + private RestTemplate restTemplate = new RestTemplate(); + + @Autowired + private ObjectMapper objectMapper; + + @Value("http://127.0.0.1:${local.management.port}${management.contextPath:}") + private String actuatorBaseURL; + + @Test + public void testShutdown() throws Exception { + + Map value = dubboShutdownMetadata.shutdown(); + + Map shutdownCounts = (Map) value.get("shutdown.count"); + + Assert.assertEquals(0, shutdownCounts.get("registries")); + Assert.assertEquals(1, shutdownCounts.get("protocols")); + Assert.assertEquals(1, shutdownCounts.get("services")); + Assert.assertEquals(0, shutdownCounts.get("references")); + + } + + @Test + public void testConfigs() { + + Map>> configsMap = dubboConfigsMetadata.configs(); + + Map> beansMetadata = configsMap.get("ApplicationConfig"); + Assert.assertEquals("dubbo-demo-application", beansMetadata.get("my-application").get("name")); + + beansMetadata = configsMap.get("ConsumerConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("MethodConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("ModuleConfig"); + Assert.assertEquals("dubbo-demo-module", beansMetadata.get("my-module").get("name")); + + beansMetadata = configsMap.get("MonitorConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("ProtocolConfig"); + Assert.assertEquals("dubbo", beansMetadata.get("my-protocol").get("name")); + + beansMetadata = configsMap.get("ProviderConfig"); + Assert.assertEquals("127.0.0.1", beansMetadata.get("my-provider").get("host")); + + beansMetadata = configsMap.get("ReferenceConfig"); + Assert.assertTrue(beansMetadata.isEmpty()); + + beansMetadata = configsMap.get("RegistryConfig"); + Assert.assertEquals("N/A", beansMetadata.get("my-registry").get("address")); + + beansMetadata = configsMap.get("ServiceConfig"); + Assert.assertFalse(beansMetadata.isEmpty()); + + } + + @Test + public void testServices() { + + Map> services = dubboServicesMetadata.services(); + + Assert.assertEquals(1, services.size()); + + Map demoServiceMeta = services.get("ServiceBean:org.apache.dubbo.spring.boot.actuate.autoconfigure.DubboEndpointAutoConfigurationTest$DemoService:1.0.0"); + + Assert.assertEquals("1.0.0", demoServiceMeta.get("version")); + + } + + @Test + public void testReferences() { + + Map> references = dubboReferencesMetadata.references(); + + Assert.assertTrue(references.isEmpty()); + + } + + @Test + public void testProperties() { + + SortedMap properties = dubboProperties.properties(); + + Assert.assertEquals("my-application", properties.get("dubbo.application.id")); + Assert.assertEquals("dubbo-demo-application", properties.get("dubbo.application.name")); + Assert.assertEquals("my-module", properties.get("dubbo.module.id")); + Assert.assertEquals("dubbo-demo-module", properties.get("dubbo.module.name")); + Assert.assertEquals("my-registry", properties.get("dubbo.registry.id")); + Assert.assertEquals("N/A", properties.get("dubbo.registry.address")); + Assert.assertEquals("my-protocol", properties.get("dubbo.protocol.id")); + Assert.assertEquals("dubbo", properties.get("dubbo.protocol.name")); + Assert.assertEquals("20880", properties.get("dubbo.protocol.port")); + Assert.assertEquals("my-provider", properties.get("dubbo.provider.id")); + Assert.assertEquals("127.0.0.1", properties.get("dubbo.provider.host")); + Assert.assertEquals("org.apache.dubbo.spring.boot.actuate.autoconfigure", properties.get("dubbo.scan.basePackages")); + } + + @Test + public void testHttpEndpoints() throws JsonProcessingException { +// testHttpEndpoint("/dubbo", dubboEndpoint::invoke); + testHttpEndpoint("/dubbo/configs", dubboConfigsMetadata::configs); + testHttpEndpoint("/dubbo/services", dubboServicesMetadata::services); + testHttpEndpoint("/dubbo/references", dubboReferencesMetadata::references); + testHttpEndpoint("/dubbo/properties", dubboProperties::properties); + } + + private void testHttpEndpoint(String actuatorURI, Supplier resultsSupplier) throws JsonProcessingException { + String actuatorURL = actuatorBaseURL + actuatorURI; + String response = restTemplate.getForObject(actuatorURL, String.class); + Assert.assertEquals(objectMapper.writeValueAsString(resultsSupplier.get()), response); + } + + + interface DemoService { + String sayHello(String name); + } + + @DubboService( + version = "${dubbo.service.version}", + application = "${dubbo.application.id}", + protocol = "${dubbo.protocol.id}", + registry = "${dubbo.registry.id}" + ) + static class DefaultDemoService implements DemoService { + + public String sayHello(String name) { + return "Hello, " + name + " (from Spring Boot)"; + } + + } + + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java new file mode 100644 index 00000000000..0c55f7f5b22 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.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.dubbo.spring.boot.actuate.health; + +import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.Status; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Map; + +/** + * {@link DubboHealthIndicator} Test + * + * @see DubboHealthIndicator + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@TestPropertySource(properties = { + "dubbo.application.id = my-application-1", + "dubbo.application.name = dubbo-demo-application-1", + "dubbo.protocol.id = dubbo-protocol", + "dubbo.protocol.name = dubbo", + "dubbo.protocol.port = 12345", + "dubbo.protocol.status = registry", + "dubbo.provider.id = dubbo-provider", + "dubbo.provider.status = server", + "management.health.dubbo.status.defaults = memory", + "management.health.dubbo.status.extras = load,threadpool" +}) +@SpringBootTest( + classes = { + DubboHealthIndicator.class, + DubboHealthIndicatorTest.class + } +) +@EnableConfigurationProperties(DubboHealthIndicatorProperties.class) +@EnableDubboConfig +public class DubboHealthIndicatorTest { + + @Autowired + private DubboHealthIndicator dubboHealthIndicator; + + @Test + public void testResolveStatusCheckerNamesMap() { + + Map statusCheckerNamesMap = dubboHealthIndicator.resolveStatusCheckerNamesMap(); + + Assert.assertEquals(5, statusCheckerNamesMap.size()); + + Assert.assertEquals("dubbo-protocol@ProtocolConfig.getStatus()", statusCheckerNamesMap.get("registry")); + Assert.assertEquals("dubbo-provider@ProviderConfig.getStatus()", statusCheckerNamesMap.get("server")); + Assert.assertEquals("management.health.dubbo.status.defaults", statusCheckerNamesMap.get("memory")); + Assert.assertEquals("management.health.dubbo.status.extras", statusCheckerNamesMap.get("load")); + Assert.assertEquals("management.health.dubbo.status.extras", statusCheckerNamesMap.get("threadpool")); + + } + + @Test + public void testHealth() { + + Health health = dubboHealthIndicator.health(); + + Assert.assertEquals(Status.UNKNOWN, health.getStatus()); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/pom.xml new file mode 100644 index 00000000000..488bc92017c --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/pom.xml @@ -0,0 +1,88 @@ + + + + + org.apache.dubbo + dubbo-spring-boot-compatible + ${revision} + ../pom.xml + + 4.0.0 + + dubbo-spring-boot-autoconfigure-compatible + Apache Dubbo Spring Boot Compatible for Spring Boot 1.x Auto-Configure + + + + + + org.springframework.boot + spring-boot-autoconfigure + true + + + + org.springframework.boot + spring-boot + true + + + + org.springframework.boot + spring-boot-starter-logging + true + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.apache.dubbo + dubbo-common + ${revision} + true + + + + org.apache.dubbo + dubbo-config-spring + ${revision} + true + + + + org.apache.dubbo + dubbo + ${revision} + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfiguration.java new file mode 100644 index 00000000000..ba668f80846 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfiguration.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.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.dubbo.config.annotation.DubboService; +import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; +import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor; +import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener; +import org.apache.dubbo.config.spring.context.DubboLifecycleComponentApplicationListener; +import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Set; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_BEAN_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_PROPERTY_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SCAN_PREFIX; + +/** + * Dubbo Auto {@link Configuration} + * + * @see DubboReference + * @see DubboService + * @see ServiceClassPostProcessor + * @see ReferenceAnnotationBeanPostProcessor + * @since 2.7.0 + */ +@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true) +@Configuration +@AutoConfigureAfter(DubboRelaxedBindingAutoConfiguration.class) +@EnableConfigurationProperties(DubboConfigurationProperties.class) +@EnableDubboConfig +public class DubboAutoConfiguration implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor { + + /** + * Creates {@link ServiceClassPostProcessor} Bean + * + * @param packagesToScan the packages to scan + * @return {@link ServiceClassPostProcessor} + */ + @ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME) + @ConditionalOnBean(name = BASE_PACKAGES_BEAN_NAME) + @Bean + public ServiceClassPostProcessor serviceClassPostProcessor(@Qualifier(BASE_PACKAGES_BEAN_NAME) + Set packagesToScan) { + return new ServiceClassPostProcessor(packagesToScan); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (applicationContext instanceof ConfigurableApplicationContext) { + ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext; + DubboLifecycleComponentApplicationListener dubboLifecycleComponentApplicationListener + = new DubboLifecycleComponentApplicationListener(applicationContext); + context.addApplicationListener(dubboLifecycleComponentApplicationListener); + + DubboBootstrapApplicationListener dubboBootstrapApplicationListener = new DubboBootstrapApplicationListener(applicationContext); + context.addApplicationListener(dubboBootstrapApplicationListener); + } + } + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + // Remove the BeanDefinitions of ApplicationListener from DubboBeanUtils#registerCommonBeans(BeanDefinitionRegistry) + // TODO Refactoring in Dubbo 2.7.9 + removeBeanDefinition(registry, DubboLifecycleComponentApplicationListener.BEAN_NAME); + removeBeanDefinition(registry, DubboBootstrapApplicationListener.BEAN_NAME); + } + + private void removeBeanDefinition(BeanDefinitionRegistry registry, String beanName) { + if (registry.containsBeanDefinition(beanName)) { + registry.removeBeanDefinition(beanName); + } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + // DO NOTHING + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java new file mode 100644 index 00000000000..f6cef20f730 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java @@ -0,0 +1,300 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ConsumerConfig; +import org.apache.dubbo.config.MetadataReportConfig; +import org.apache.dubbo.config.ModuleConfig; +import org.apache.dubbo.config.MonitorConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ProviderConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.spring.ConfigCenterBean; +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.DEFAULT_MULTIPLE_CONFIG_PROPERTY_VALUE; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX; + +/** + * Dubbo {@link ConfigurationProperties Config Properties} only used to generate JSON metadata(non-public class) + * + * @since 2.7.1 + */ +@ConfigurationProperties(DUBBO_PREFIX) +public class DubboConfigurationProperties { + + @NestedConfigurationProperty + private Config config = new Config(); + + @NestedConfigurationProperty + private Scan scan = new Scan(); + + // Single Config Bindings + @NestedConfigurationProperty + private ApplicationConfig application = new ApplicationConfig(); + + @NestedConfigurationProperty + private ModuleConfig module = new ModuleConfig(); + + @NestedConfigurationProperty + private RegistryConfig registry = new RegistryConfig(); + + @NestedConfigurationProperty + private ProtocolConfig protocol = new ProtocolConfig(); + + @NestedConfigurationProperty + private MonitorConfig monitor = new MonitorConfig(); + + @NestedConfigurationProperty + private ProviderConfig provider = new ProviderConfig(); + + @NestedConfigurationProperty + private ConsumerConfig consumer = new ConsumerConfig(); + + @NestedConfigurationProperty + private ConfigCenterBean configCenter = new ConfigCenterBean(); + + @NestedConfigurationProperty + private MetadataReportConfig metadataReport = new MetadataReportConfig(); + + // Multiple Config Bindings + + private Map modules = new LinkedHashMap<>(); + + private Map registries = new LinkedHashMap<>(); + + private Map protocols = new LinkedHashMap<>(); + + private Map monitors = new LinkedHashMap<>(); + + private Map providers = new LinkedHashMap<>(); + + private Map consumers = new LinkedHashMap<>(); + + private Map configCenters = new LinkedHashMap<>(); + + private Map metadataReports = new LinkedHashMap<>(); + + public Config getConfig() { + return config; + } + + public void setConfig(Config config) { + this.config = config; + } + + public Scan getScan() { + return scan; + } + + public void setScan(Scan scan) { + this.scan = scan; + } + + public ApplicationConfig getApplication() { + return application; + } + + public void setApplication(ApplicationConfig application) { + this.application = application; + } + + public ModuleConfig getModule() { + return module; + } + + public void setModule(ModuleConfig module) { + this.module = module; + } + + public RegistryConfig getRegistry() { + return registry; + } + + public void setRegistry(RegistryConfig registry) { + this.registry = registry; + } + + public ProtocolConfig getProtocol() { + return protocol; + } + + public void setProtocol(ProtocolConfig protocol) { + this.protocol = protocol; + } + + public MonitorConfig getMonitor() { + return monitor; + } + + public void setMonitor(MonitorConfig monitor) { + this.monitor = monitor; + } + + public ProviderConfig getProvider() { + return provider; + } + + public void setProvider(ProviderConfig provider) { + this.provider = provider; + } + + public ConsumerConfig getConsumer() { + return consumer; + } + + public void setConsumer(ConsumerConfig consumer) { + this.consumer = consumer; + } + + public ConfigCenterBean getConfigCenter() { + return configCenter; + } + + public void setConfigCenter(ConfigCenterBean configCenter) { + this.configCenter = configCenter; + } + + public MetadataReportConfig getMetadataReport() { + return metadataReport; + } + + public void setMetadataReport(MetadataReportConfig metadataReport) { + this.metadataReport = metadataReport; + } + + public Map getModules() { + return modules; + } + + public void setModules(Map modules) { + this.modules = modules; + } + + public Map getRegistries() { + return registries; + } + + public void setRegistries(Map registries) { + this.registries = registries; + } + + public Map getProtocols() { + return protocols; + } + + public void setProtocols(Map protocols) { + this.protocols = protocols; + } + + public Map getMonitors() { + return monitors; + } + + public void setMonitors(Map monitors) { + this.monitors = monitors; + } + + public Map getProviders() { + return providers; + } + + public void setProviders(Map providers) { + this.providers = providers; + } + + public Map getConsumers() { + return consumers; + } + + public void setConsumers(Map consumers) { + this.consumers = consumers; + } + + public Map getConfigCenters() { + return configCenters; + } + + public void setConfigCenters(Map configCenters) { + this.configCenters = configCenters; + } + + public Map getMetadataReports() { + return metadataReports; + } + + public void setMetadataReports(Map metadataReports) { + this.metadataReports = metadataReports; + } + + static class Config { + + /** + * Indicates multiple properties binding from externalized configuration or not. + */ + private boolean multiple = DEFAULT_MULTIPLE_CONFIG_PROPERTY_VALUE; + + /** + * The property name of override Dubbo config + */ + private boolean override = DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE; + + public boolean isOverride() { + return override; + } + + public void setOverride(boolean override) { + this.override = override; + } + + public boolean isMultiple() { + return multiple; + } + + public void setMultiple(boolean multiple) { + this.multiple = multiple; + } + } + + static class Scan { + + /** + * The basePackages to scan , the multiple-value is delimited by comma + * + * @see EnableDubbo#scanBasePackages() + */ + private Set basePackages = new LinkedHashSet<>(); + + public Set getBasePackages() { + return basePackages; + } + + public void setBasePackages(Set basePackages) { + this.basePackages = basePackages; + } + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBindingAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBindingAutoConfiguration.java new file mode 100644 index 00000000000..3cefb46e982 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBindingAutoConfiguration.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.dubbo.spring.boot.autoconfigure; + +import com.alibaba.spring.context.config.ConfigurationBeanBinder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertyResolver; + +import java.util.Set; + +import static java.util.Collections.emptySet; +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_BEAN_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_PROPERTY_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SCAN_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME; +import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE; + +/** + * Dubbo Relaxed Binding Auto-{@link Configuration} for Spring Boot 1.x + */ +@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true) +@ConditionalOnClass(name = "org.springframework.boot.bind.RelaxedPropertyResolver") +@Configuration +public class DubboRelaxedBindingAutoConfiguration { + + public PropertyResolver dubboScanBasePackagesPropertyResolver(Environment environment) { + return new RelaxedPropertyResolver(environment, DUBBO_SCAN_PREFIX); + } + + /** + * The bean is used to scan the packages of Dubbo Service classes + * + * @param environment {@link Environment} instance + * @return non-null {@link Set} + * @since 2.7.8 + */ + @ConditionalOnMissingBean(name = BASE_PACKAGES_BEAN_NAME) + @Bean(name = BASE_PACKAGES_BEAN_NAME) + public Set dubboBasePackages(Environment environment) { + PropertyResolver propertyResolver = dubboScanBasePackagesPropertyResolver(environment); + return propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet()); + } + + @ConditionalOnMissingBean(name = RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME, value = ConfigurationBeanBinder.class) + @Bean(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME) + @Scope(scopeName = SCOPE_PROTOTYPE) + public ConfigurationBeanBinder relaxedDubboConfigBinder() { + return new RelaxedDubboConfigBinder(); + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinder.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinder.java new file mode 100644 index 00000000000..fcd461a7895 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinder.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.spring.context.properties.DubboConfigBinder; + +import com.alibaba.spring.context.config.ConfigurationBeanBinder; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.boot.bind.RelaxedDataBinder; + +import java.util.Map; + + +/** + * Spring Boot Relaxed {@link DubboConfigBinder} implementation + * + * @since 2.7.0 + */ +class RelaxedDubboConfigBinder implements ConfigurationBeanBinder { + + @Override + public void bind(Map configurationProperties, boolean ignoreUnknownFields, + boolean ignoreInvalidFields, Object configurationBean) { + RelaxedDataBinder relaxedDataBinder = new RelaxedDataBinder(configurationBean); + // Set ignored* + relaxedDataBinder.setIgnoreInvalidFields(ignoreInvalidFields); + relaxedDataBinder.setIgnoreUnknownFields(ignoreUnknownFields); + // Get properties under specified prefix from PropertySources + // Convert Map to MutablePropertyValues + MutablePropertyValues propertyValues = new MutablePropertyValues(configurationProperties); + // Bind + relaxedDataBinder.bind(propertyValues); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java new file mode 100644 index 00000000000..9b3ee16a1f7 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.beans.factory.config; + +import org.apache.dubbo.config.ServiceConfig; +import org.apache.dubbo.config.spring.ServiceBean; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static org.springframework.util.ClassUtils.getUserClass; +import static org.springframework.util.ClassUtils.isAssignable; + +/** + * The post-processor for resolving the id conflict of {@link ServiceBean} when an interface is + * implemented by multiple services with different groups or versions that are exported on one provider + *

    + * Current implementation is a temporary resolution, and will be removed in the future. + * + * @see CommonAnnotationBeanPostProcessor + * @since 2.7.7 + * @deprecated + */ +public class ServiceBeanIdConflictProcessor implements MergedBeanDefinitionPostProcessor, DisposableBean, PriorityOrdered { + + /** + * The key is the class names of interfaces that were exported by {@link ServiceBean} + * The value is bean names of {@link ServiceBean} or {@link ServiceConfig}. + */ + private Map interfaceNamesToBeanNames = new HashMap<>(); + + /** + * Holds the bean names of {@link ServiceBean} or {@link ServiceConfig}. + */ + private Set conflictedBeanNames = new LinkedHashSet<>(); + + @Override + public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { + // Get raw bean type + Class rawBeanType = getUserClass(beanType); + if (isAssignable(ServiceConfig.class, rawBeanType)) { // ServiceConfig type or sub-type + String interfaceName = (String) beanDefinition.getPropertyValues().get("interface"); + String mappedBeanName = interfaceNamesToBeanNames.putIfAbsent(interfaceName, beanName); + // If mapped bean name exists and does not equal current bean name + if (mappedBeanName != null && !mappedBeanName.equals(beanName)) { + // conflictedBeanNames will record current bean name. + conflictedBeanNames.add(beanName); + } + } + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (conflictedBeanNames.contains(beanName) && bean instanceof ServiceConfig) { + ServiceConfig serviceConfig = (ServiceConfig) bean; + if (isConflictedServiceConfig(serviceConfig)) { + // Set id as the bean name + serviceConfig.setId(beanName); + } + + } + return bean; + } + + private boolean isConflictedServiceConfig(ServiceConfig serviceConfig) { + return Objects.equals(serviceConfig.getId(), serviceConfig.getInterface()); + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + /** + * Keep the order being higher than {@link CommonAnnotationBeanPostProcessor#getOrder()} that is + * {@link Ordered#LOWEST_PRECEDENCE} + * + * @return {@link Ordered#LOWEST_PRECEDENCE} +1 + */ + @Override + public int getOrder() { + return LOWEST_PRECEDENCE + 1; + } + + @Override + public void destroy() throws Exception { + interfaceNamesToBeanNames.clear(); + conflictedBeanNames.clear(); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/DubboApplicationContextInitializer.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/DubboApplicationContextInitializer.java new file mode 100644 index 00000000000..ed8c96c0200 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/DubboApplicationContextInitializer.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.context; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.Ordered; + +/** + * Dubbo {@link ApplicationContextInitializer} implementation + * + * @see ApplicationContextInitializer + * @since 2.7.1 + */ +public class DubboApplicationContextInitializer implements ApplicationContextInitializer, Ordered { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + overrideBeanDefinitions(applicationContext); + } + + private void overrideBeanDefinitions(ConfigurableApplicationContext applicationContext) { + // @since 2.7.8 OverrideBeanDefinitionRegistryPostProcessor has been removed + // applicationContext.addBeanFactoryPostProcessor(new OverrideBeanDefinitionRegistryPostProcessor()); + // @since 2.7.5 DubboConfigBeanDefinitionConflictProcessor has been removed + // @see {@link DubboConfigBeanDefinitionConflictApplicationListener} + // applicationContext.addBeanFactoryPostProcessor(new DubboConfigBeanDefinitionConflictProcessor()); + // TODO Add some components in the future ( after 2.7.8 ) + } + + @Override + public int getOrder() { + return HIGHEST_PRECEDENCE; + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java new file mode 100644 index 00000000000..a897a0fac1a --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.context.event; + +import org.apache.dubbo.common.lang.ShutdownHookCallbacks; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.SmartApplicationListener; +import org.springframework.util.ClassUtils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static org.springframework.util.ObjectUtils.containsElement; + +/** + * Awaiting Non-Web Spring Boot {@link ApplicationListener} + * + * @since 2.7.0 + */ +public class AwaitingNonWebApplicationListener implements SmartApplicationListener { + + private static final String[] WEB_APPLICATION_CONTEXT_CLASSES = new String[]{ + "org.springframework.web.context.WebApplicationContext", + "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext" + }; + + private static final Logger logger = LoggerFactory.getLogger(AwaitingNonWebApplicationListener.class); + + private static final Class[] SUPPORTED_APPLICATION_EVENTS = + of(ApplicationReadyEvent.class, ContextClosedEvent.class); + + private static final AtomicBoolean awaited = new AtomicBoolean(false); + + private static final Integer UNDEFINED_ID = Integer.valueOf(-1); + + /** + * Target the application id + */ + private static final AtomicInteger applicationContextId = new AtomicInteger(UNDEFINED_ID); + + private static final Lock lock = new ReentrantLock(); + + private static final Condition condition = lock.newCondition(); + + private static final ExecutorService executorService = newSingleThreadExecutor(); + + private static T[] of(T... values) { + return values; + } + + static AtomicBoolean getAwaited() { + return awaited; + } + + @Override + public boolean supportsEventType(Class eventType) { + return containsElement(SUPPORTED_APPLICATION_EVENTS, eventType); + } + + @Override + public boolean supportsSourceType(Class sourceType) { + return true; + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationReadyEvent) { + onApplicationReadyEvent((ApplicationReadyEvent) event); + } + } + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; + } + + protected void onApplicationReadyEvent(ApplicationReadyEvent event) { + + final ConfigurableApplicationContext applicationContext = event.getApplicationContext(); + + if (!isRootApplicationContext(applicationContext) || isWebApplication(applicationContext)) { + return; + } + + if (applicationContextId.compareAndSet(UNDEFINED_ID, applicationContext.hashCode())) { + await(); + releaseOnExit(); + } + } + + /** + * @since 2.7.8 + */ + private void releaseOnExit() { + ShutdownHookCallbacks.INSTANCE.addCallback(this::release); + } + + private boolean isRootApplicationContext(ApplicationContext applicationContext) { + return applicationContext.getParent() == null; + } + + private boolean isWebApplication(ApplicationContext applicationContext) { + boolean webApplication = false; + for (String contextClass : WEB_APPLICATION_CONTEXT_CLASSES) { + if (isAssignable(contextClass, applicationContext.getClass(), applicationContext.getClassLoader())) { + webApplication = true; + break; + } + } + return webApplication; + } + + private static boolean isAssignable(String target, Class type, ClassLoader classLoader) { + try { + return ClassUtils.resolveClassName(target, classLoader).isAssignableFrom(type); + } catch (Throwable ex) { + return false; + } + } + + protected void await() { + + // has been waited, return immediately + if (awaited.get()) { + return; + } + + if (!executorService.isShutdown()) { + executorService.execute(() -> executeMutually(() -> { + while (!awaited.get()) { + if (logger.isInfoEnabled()) { + logger.info(" [Dubbo] Current Spring Boot Application is await..."); + } + try { + condition.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + })); + } + } + + protected void release() { + executeMutually(() -> { + while (awaited.compareAndSet(false, true)) { + if (logger.isInfoEnabled()) { + logger.info(" [Dubbo] Current Spring Boot Application is about to shutdown..."); + } + condition.signalAll(); + // @since 2.7.8 method shutdown() is combined into the method release() + shutdown(); + } + }); + } + + private void shutdown() { + if (!executorService.isShutdown()) { + // Shutdown executorService + executorService.shutdown(); + } + } + + private void executeMutually(Runnable runnable) { + try { + lock.lock(); + runnable.run(); + } finally { + lock.unlock(); + } + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListener.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListener.java new file mode 100644 index 00000000000..f18c8b2e4e7 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListener.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.context.event; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; + +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors; +import static org.springframework.context.ConfigurableApplicationContext.ENVIRONMENT_BEAN_NAME; + +/** + * The {@link ApplicationListener} class for Dubbo Config {@link BeanDefinition Bean Definition} to resolve conflict + * @see BeanDefinition + * @see ApplicationListener + * @since 2.7.5 + */ +public class DubboConfigBeanDefinitionConflictApplicationListener implements ApplicationListener, + Ordered { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + ApplicationContext applicationContext = event.getApplicationContext(); + BeanDefinitionRegistry registry = getBeanDefinitionRegistry(applicationContext); + resolveUniqueApplicationConfigBean(registry, applicationContext); + } + + private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext applicationContext) { + AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); + if (beanFactory instanceof BeanDefinitionRegistry) { + return (BeanDefinitionRegistry) beanFactory; + } + throw new IllegalStateException(""); + } + + /** + * Resolve the unique {@link ApplicationConfig} Bean + * @param registry {@link BeanDefinitionRegistry} instance + * @param beanFactory {@link ConfigurableListableBeanFactory} instance + * @see EnableDubboConfig + */ + private void resolveUniqueApplicationConfigBean(BeanDefinitionRegistry registry, ListableBeanFactory beanFactory) { + + String[] beansNames = beanNamesForTypeIncludingAncestors(beanFactory, ApplicationConfig.class); + + if (beansNames.length < 2) { // If the number of ApplicationConfig beans is less than two, return immediately. + return; + } + + Environment environment = beanFactory.getBean(ENVIRONMENT_BEAN_NAME, Environment.class); + + // Remove ApplicationConfig Beans that are configured by "dubbo.application.*" + Stream.of(beansNames) + .filter(beansName -> isConfiguredApplicationConfigBeanName(environment, beansName)) + .forEach(registry::removeBeanDefinition); + + beansNames = beanNamesForTypeIncludingAncestors(beanFactory, ApplicationConfig.class); + + if (beansNames.length > 1) { + throw new IllegalStateException(String.format("There are more than one instances of %s, whose bean definitions : %s", + ApplicationConfig.class.getSimpleName(), + Stream.of(beansNames) + .map(registry::getBeanDefinition) + .collect(Collectors.toList())) + ); + } + } + + private boolean isConfiguredApplicationConfigBeanName(Environment environment, String beanName) { + boolean removed = BeanFactoryUtils.isGeneratedBeanName(beanName) + // Dubbo ApplicationConfig id as bean name + || Objects.equals(beanName, environment.getProperty("dubbo.application.id")); + + if (removed) { + if (logger.isDebugEnabled()) { + logger.debug("The {} bean [ name : {} ] has been removed!", ApplicationConfig.class.getSimpleName(), beanName); + } + } + + return removed; + } + + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListener.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListener.java new file mode 100644 index 00000000000..45d98a6a779 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListener.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.dubbo.spring.boot.context.event; + +import org.apache.dubbo.common.utils.ConfigUtils; +import org.apache.dubbo.config.AbstractConfig; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; + +import java.util.SortedMap; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE; +import static org.apache.dubbo.spring.boot.util.DubboUtils.OVERRIDE_CONFIG_FULL_PROPERTY_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.filterDubboProperties; + +/** + * {@link ApplicationListener} to override the dubbo properties from {@link Environment}into + * {@link ConfigUtils#getProperties() Dubbo Config}. + * {@link AbstractConfig Dubbo Config} on {@link ApplicationEnvironmentPreparedEvent}. + *

    + * + * @see ConfigUtils + * @since 2.7.0 + */ +@Order // LOWEST_PRECEDENCE Make sure last execution +public class OverrideDubboConfigApplicationListener implements ApplicationListener { + + @Override + public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + + /** + * Gets Logger After LoggingSystem configuration ready + * @see LoggingApplicationListener + */ + final Logger logger = LoggerFactory.getLogger(getClass()); + + ConfigurableEnvironment environment = event.getEnvironment(); + + boolean override = environment.getProperty(OVERRIDE_CONFIG_FULL_PROPERTY_NAME, boolean.class, + DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE); + + if (override) { + + SortedMap dubboProperties = filterDubboProperties(environment); + + ConfigUtils.getProperties().putAll(dubboProperties); + + if (logger.isInfoEnabled()) { + logger.info("Dubbo Config was overridden by externalized configuration {}", dubboProperties); + } + } else { + if (logger.isInfoEnabled()) { + logger.info("Disable override Dubbo Config caused by property {} = {}", OVERRIDE_CONFIG_FULL_PROPERTY_NAME, override); + } + } + + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListener.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListener.java new file mode 100644 index 00000000000..7aa0d8d3e9f --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListener.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.dubbo.spring.boot.context.event; + +import org.apache.dubbo.common.Version; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_GITHUB_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_MAILING_LIST; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SPRING_BOOT_GITHUB_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.LINE_SEPARATOR; + +/** + * Dubbo Welcome Logo {@link ApplicationListener} + * + * @see ApplicationListener + * @since 2.7.0 + */ +@Order(Ordered.HIGHEST_PRECEDENCE + 20 + 1) // After LoggingApplicationListener#DEFAULT_ORDER +public class WelcomeLogoApplicationListener implements ApplicationListener { + + private static AtomicBoolean processed = new AtomicBoolean(false); + + @Override + public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + + // Skip if processed before, prevent duplicated execution in Hierarchical ApplicationContext + if (processed.get()) { + return; + } + + /** + * Gets Logger After LoggingSystem configuration ready + * @see LoggingApplicationListener + */ + final Logger logger = LoggerFactory.getLogger(getClass()); + + String bannerText = buildBannerText(); + + if (logger.isInfoEnabled()) { + logger.info(bannerText); + } else { + System.out.print(bannerText); + } + + // mark processed to be true + processed.compareAndSet(false, true); + } + + String buildBannerText() { + + StringBuilder bannerTextBuilder = new StringBuilder(); + + bannerTextBuilder + .append(LINE_SEPARATOR) + .append(LINE_SEPARATOR) + .append(" :: Dubbo Spring Boot (v").append(Version.getVersion(getClass(), Version.getVersion())).append(") : ") + .append(DUBBO_SPRING_BOOT_GITHUB_URL) + .append(LINE_SEPARATOR) + .append(" :: Dubbo (v").append(Version.getVersion()).append(") : ") + .append(DUBBO_GITHUB_URL) + .append(LINE_SEPARATOR) + .append(" :: Discuss group : ") + .append(DUBBO_MAILING_LIST) + .append(LINE_SEPARATOR) + ; + + return bannerTextBuilder.toString(); + + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java new file mode 100644 index 00000000000..6fb1a881456 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.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.dubbo.spring.boot.env; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_APPLICATION_NAME_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_APPLICATION_QOS_ENABLE_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_CONFIG_MULTIPLE_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.SPRING_APPLICATION_NAME_PROPERTY; + +/** + * The lowest precedence {@link EnvironmentPostProcessor} processes + * {@link SpringApplication#setDefaultProperties(Properties) Spring Boot default properties} for Dubbo + * as late as possible before {@link ConfigurableApplicationContext#refresh() application context refresh}. + */ +public class DubboDefaultPropertiesEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + + /** + * The name of default {@link PropertySource} defined in SpringApplication#configurePropertySources method. + */ + public static final String PROPERTY_SOURCE_NAME = "defaultProperties"; + + /** + * The property name of "spring.main.allow-bean-definition-overriding". + * Please refer to: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes#bean-overriding + */ + public static final String ALLOW_BEAN_DEFINITION_OVERRIDING_PROPERTY = "spring.main.allow-bean-definition-overriding"; + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + MutablePropertySources propertySources = environment.getPropertySources(); + Map defaultProperties = createDefaultProperties(environment); + if (!CollectionUtils.isEmpty(defaultProperties)) { + addOrReplace(propertySources, defaultProperties); + } + } + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; + } + + private Map createDefaultProperties(ConfigurableEnvironment environment) { + Map defaultProperties = new HashMap<>(); + setDubboApplicationNameProperty(environment, defaultProperties); + setDubboConfigMultipleProperty(defaultProperties); + setDubboApplicationQosEnableProperty(defaultProperties); + setAllowBeanDefinitionOverriding(defaultProperties); + return defaultProperties; + } + + private void setDubboApplicationNameProperty(Environment environment, Map defaultProperties) { + String springApplicationName = environment.getProperty(SPRING_APPLICATION_NAME_PROPERTY); + if (StringUtils.hasLength(springApplicationName) + && !environment.containsProperty(DUBBO_APPLICATION_NAME_PROPERTY)) { + defaultProperties.put(DUBBO_APPLICATION_NAME_PROPERTY, springApplicationName); + } + } + + private void setDubboConfigMultipleProperty(Map defaultProperties) { + defaultProperties.put(DUBBO_CONFIG_MULTIPLE_PROPERTY, Boolean.TRUE.toString()); + } + + private void setDubboApplicationQosEnableProperty(Map defaultProperties) { + defaultProperties.put(DUBBO_APPLICATION_QOS_ENABLE_PROPERTY, Boolean.FALSE.toString()); + } + + /** + * Set {@link #ALLOW_BEAN_DEFINITION_OVERRIDING_PROPERTY "spring.main.allow-bean-definition-overriding"} to be + * true as default. + * + * @param defaultProperties the default {@link Properties properties} + * @see #ALLOW_BEAN_DEFINITION_OVERRIDING_PROPERTY + * @since 2.7.1 + */ + private void setAllowBeanDefinitionOverriding(Map defaultProperties) { + defaultProperties.put(ALLOW_BEAN_DEFINITION_OVERRIDING_PROPERTY, Boolean.TRUE.toString()); + } + + /** + * Copy from BusEnvironmentPostProcessor#addOrReplace(MutablePropertySources, Map) + * + * @param propertySources {@link MutablePropertySources} + * @param map Default Dubbo Properties + */ + private void addOrReplace(MutablePropertySources propertySources, + Map map) { + MapPropertySource target = null; + if (propertySources.contains(PROPERTY_SOURCE_NAME)) { + PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); + if (source instanceof MapPropertySource) { + target = (MapPropertySource) source; + for (String key : map.keySet()) { + if (!target.containsProperty(key)) { + target.getSource().put(key, map.get(key)); + } + } + } + } + if (target == null) { + target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); + } + if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { + propertySources.addLast(target); + } + } +} \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/DubboUtils.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/DubboUtils.java new file mode 100644 index 00000000000..fdc396de043 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/DubboUtils.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.dubbo.spring.boot.util; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor; +import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor; +import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; +import org.apache.dubbo.config.spring.context.properties.DubboConfigBinder; + +import org.springframework.boot.context.ContextIdApplicationContextInitializer; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertyResolver; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * The utilities class for Dubbo + * + * @since 2.7.0 + */ +public abstract class DubboUtils { + + /** + * line separator + */ + public static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + + /** + * The separator of property name + */ + public static final String PROPERTY_NAME_SEPARATOR = "."; + + /** + * The prefix of property name of Dubbo + */ + public static final String DUBBO_PREFIX = "dubbo"; + + /** + * The prefix of property name for Dubbo scan + */ + public static final String DUBBO_SCAN_PREFIX = DUBBO_PREFIX + PROPERTY_NAME_SEPARATOR + "scan" + PROPERTY_NAME_SEPARATOR; + + /** + * The prefix of property name for Dubbo Config + */ + public static final String DUBBO_CONFIG_PREFIX = DUBBO_PREFIX + PROPERTY_NAME_SEPARATOR + "config" + PROPERTY_NAME_SEPARATOR; + + /** + * The property name of base packages to scan + *

    + * The default value is empty set. + */ + public static final String BASE_PACKAGES_PROPERTY_NAME = "base-packages"; + + /** + * The property name of multiple properties binding from externalized configuration + *

    + * The default value is {@link #DEFAULT_MULTIPLE_CONFIG_PROPERTY_VALUE} + * + * @deprecated 2.7.8 It will be remove in the future, {@link EnableDubboConfig} instead + */ + @Deprecated + public static final String MULTIPLE_CONFIG_PROPERTY_NAME = "multiple"; + + /** + * The default value of multiple properties binding from externalized configuration + * + * @deprecated 2.7.8 It will be remove in the future + */ + @Deprecated + public static final boolean DEFAULT_MULTIPLE_CONFIG_PROPERTY_VALUE = true; + + /** + * The property name of override Dubbo config + *

    + * The default value is {@link #DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE} + */ + public static final String OVERRIDE_CONFIG_FULL_PROPERTY_NAME = DUBBO_CONFIG_PREFIX + "override"; + + /** + * The default property value of override Dubbo config + */ + public static final boolean DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE = true; + + + /** + * The github URL of Dubbo Spring Boot + */ + public static final String DUBBO_SPRING_BOOT_GITHUB_URL = "https://github.com/apache/dubbo-spring-boot-project"; + + /** + * The git URL of Dubbo Spring Boot + */ + public static final String DUBBO_SPRING_BOOT_GIT_URL = "https://github.com/apache/dubbo-spring-boot-project.git"; + + /** + * The issues of Dubbo Spring Boot + */ + public static final String DUBBO_SPRING_BOOT_ISSUES_URL = "https://github.com/apache/dubbo-spring-boot-project/issues"; + + /** + * The github URL of Dubbo + */ + public static final String DUBBO_GITHUB_URL = "https://github.com/apache/dubbo"; + + /** + * The google group URL of Dubbo + */ + public static final String DUBBO_MAILING_LIST = "dev@dubbo.apache.org"; + + /** + * The bean name of Relaxed-binding {@link DubboConfigBinder} + */ + public static final String RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME = "relaxedDubboConfigBinder"; + + /** + * The bean name of {@link PropertyResolver} for {@link ServiceAnnotationBeanPostProcessor}'s base-packages + * + * @deprecated 2.7.8 It will be remove in the future, please use {@link #BASE_PACKAGES_BEAN_NAME} + */ + @Deprecated + public static final String BASE_PACKAGES_PROPERTY_RESOLVER_BEAN_NAME = "dubboScanBasePackagesPropertyResolver"; + + /** + * The bean name of {@link Set} presenting {@link ServiceClassPostProcessor}'s base-packages + * + * @since 2.7.8 + */ + public static final String BASE_PACKAGES_BEAN_NAME = "dubbo-service-class-base-packages"; + + /** + * The property name of Spring Application + * + * @see ContextIdApplicationContextInitializer + * @since 2.7.1 + */ + public static final String SPRING_APPLICATION_NAME_PROPERTY = "spring.application.name"; + + /** + * The property id of {@link ApplicationConfig} Bean + * + * @see EnableDubboConfig + * @since 2.7.1 + */ + public static final String DUBBO_APPLICATION_ID_PROPERTY = "dubbo.application.id"; + + /** + * The property name of {@link ApplicationConfig} + * + * @see EnableDubboConfig + * @since 2.7.1 + */ + public static final String DUBBO_APPLICATION_NAME_PROPERTY = "dubbo.application.name"; + + /** + * The property name of {@link ApplicationConfig#getQosEnable() application's QOS enable} + * + * @since 2.7.1 + */ + public static final String DUBBO_APPLICATION_QOS_ENABLE_PROPERTY = "dubbo.application.qos-enable"; + + /** + * The property name of {@link EnableDubboConfig#multiple() @EnableDubboConfig.multiple()} + * + * @since 2.7.1 + */ + public static final String DUBBO_CONFIG_MULTIPLE_PROPERTY = "dubbo.config.multiple"; + + + /** + * Filters Dubbo Properties from {@link ConfigurableEnvironment} + * + * @param environment {@link ConfigurableEnvironment} + * @return Read-only SortedMap + */ + public static SortedMap filterDubboProperties(ConfigurableEnvironment environment) { + + SortedMap dubboProperties = new TreeMap<>(); + + Map properties = EnvironmentUtils.extractProperties(environment); + + for (Map.Entry entry : properties.entrySet()) { + String propertyName = entry.getKey(); + + if (propertyName.startsWith(DUBBO_PREFIX + PROPERTY_NAME_SEPARATOR) + && entry.getValue() != null) { + dubboProperties.put(propertyName, environment.resolvePlaceholders(entry.getValue().toString())); + } + + } + + return Collections.unmodifiableSortedMap(dubboProperties); + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/EnvironmentUtils.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/EnvironmentUtils.java new file mode 100644 index 00000000000..4af16edf556 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/EnvironmentUtils.java @@ -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. + */ +package org.apache.dubbo.spring.boot.util; + +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.util.ObjectUtils; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * The utilities class for {@link Environment} + * + * @see Environment + * @since 2.7.0 + */ +public abstract class EnvironmentUtils { + + /** + * Extras The properties from {@link ConfigurableEnvironment} + * + * @param environment {@link ConfigurableEnvironment} + * @return Read-only Map + */ + public static Map extractProperties(ConfigurableEnvironment environment) { + return Collections.unmodifiableMap(doExtraProperties(environment)); + } + +// /** +// * Gets {@link PropertySource} Map , the {@link PropertySource#getName()} as key +// * +// * @param environment {@link ConfigurableEnvironment} +// * @return Read-only Map +// */ +// public static Map> getPropertySources(ConfigurableEnvironment environment) { +// return Collections.unmodifiableMap(doGetPropertySources(environment)); +// } + + private static Map doExtraProperties(ConfigurableEnvironment environment) { + + Map properties = new LinkedHashMap<>(); // orderly + + Map> map = doGetPropertySources(environment); + + for (PropertySource source : map.values()) { + + if (source instanceof EnumerablePropertySource) { + + EnumerablePropertySource propertySource = (EnumerablePropertySource) source; + + String[] propertyNames = propertySource.getPropertyNames(); + + if (ObjectUtils.isEmpty(propertyNames)) { + continue; + } + + for (String propertyName : propertyNames) { + + if (!properties.containsKey(propertyName)) { // put If absent + properties.put(propertyName, propertySource.getProperty(propertyName)); + } + + } + + } + + } + + return properties; + + } + + private static Map> doGetPropertySources(ConfigurableEnvironment environment) { + Map> map = new LinkedHashMap>(); + MutablePropertySources sources = environment.getPropertySources(); + for (PropertySource source : sources) { + extract("", map, source); + } + return map; + } + + private static void extract(String root, Map> map, + PropertySource source) { + if (source instanceof CompositePropertySource) { + for (PropertySource nest : ((CompositePropertySource) source) + .getPropertySources()) { + extract(source.getName() + ":", map, nest); + } + } else { + map.put(root + source.getName(), source); + } + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/resources/META-INF/spring.factories b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000000..6cf0f7f9b76 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/resources/META-INF/spring.factories @@ -0,0 +1,12 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration,\ +org.apache.dubbo.spring.boot.autoconfigure.DubboRelaxedBindingAutoConfiguration +org.springframework.context.ApplicationListener=\ +org.apache.dubbo.spring.boot.context.event.OverrideDubboConfigApplicationListener,\ +org.apache.dubbo.spring.boot.context.event.DubboConfigBeanDefinitionConflictApplicationListener,\ +org.apache.dubbo.spring.boot.context.event.WelcomeLogoApplicationListener,\ +org.apache.dubbo.spring.boot.context.event.AwaitingNonWebApplicationListener +org.springframework.boot.env.EnvironmentPostProcessor=\ +org.apache.dubbo.spring.boot.env.DubboDefaultPropertiesEnvironmentPostProcessor +org.springframework.context.ApplicationContextInitializer=\ +org.apache.dubbo.spring.boot.context.DubboApplicationContextInitializer \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/TestSuite.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/TestSuite.java new file mode 100644 index 00000000000..d6f2bb99a1b --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/TestSuite.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot; + +import org.apache.dubbo.spring.boot.autoconfigure.CompatibleDubboAutoConfigurationTest; +import org.apache.dubbo.spring.boot.autoconfigure.CompatibleDubboAutoConfigurationTestWithoutProperties; +import org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfigurationOnMultipleConfigTest; +import org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfigurationOnSingleConfigTest; +import org.apache.dubbo.spring.boot.autoconfigure.RelaxedDubboConfigBinderTest; +import org.apache.dubbo.spring.boot.context.event.AwaitingNonWebApplicationListenerTest; +import org.apache.dubbo.spring.boot.context.event.DubboConfigBeanDefinitionConflictApplicationListenerTest; +import org.apache.dubbo.spring.boot.context.event.OverrideDubboConfigApplicationListenerDisableTest; +import org.apache.dubbo.spring.boot.context.event.OverrideDubboConfigApplicationListenerTest; +import org.apache.dubbo.spring.boot.context.event.WelcomeLogoApplicationListenerTest; +import org.apache.dubbo.spring.boot.env.DubboDefaultPropertiesEnvironmentPostProcessorTest; +import org.apache.dubbo.spring.boot.util.DubboUtilsTest; +import org.apache.dubbo.spring.boot.util.EnvironmentUtilsTest; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + CompatibleDubboAutoConfigurationTest.class, + CompatibleDubboAutoConfigurationTestWithoutProperties.class, + DubboAutoConfigurationOnMultipleConfigTest.class, + DubboAutoConfigurationOnSingleConfigTest.class, + RelaxedDubboConfigBinderTest.class, + AwaitingNonWebApplicationListenerTest.class, + DubboConfigBeanDefinitionConflictApplicationListenerTest.class, + OverrideDubboConfigApplicationListenerDisableTest.class, + OverrideDubboConfigApplicationListenerTest.class, + WelcomeLogoApplicationListenerTest.class, + DubboDefaultPropertiesEnvironmentPostProcessorTest.class, + DubboUtilsTest.class, + EnvironmentUtilsTest.class +}) +public class TestSuite { +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTest.java new file mode 100644 index 00000000000..73c1f6f3c49 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; +import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.PropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * {@link DubboAutoConfiguration} Test + * @see DubboAutoConfiguration + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = { + CompatibleDubboAutoConfigurationTest.class +}, properties = { + "dubbo.scan.base-packages = org.apache.dubbo.spring.boot.autoconfigure" +}) +@EnableAutoConfiguration +@PropertySource(value = "classpath:/META-INF/dubbo.properties") +public class CompatibleDubboAutoConfigurationTest { + + @Autowired + private ObjectProvider serviceClassPostProcessor; + + @Autowired + private ObjectProvider referenceAnnotationBeanPostProcessor; + + @Test + public void testBeans() { + Assert.assertNotNull(serviceClassPostProcessor); + Assert.assertNotNull(serviceClassPostProcessor.getIfAvailable()); + Assert.assertNotNull(referenceAnnotationBeanPostProcessor); + Assert.assertNotNull(referenceAnnotationBeanPostProcessor.getIfAvailable()); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTestWithoutProperties.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTestWithoutProperties.java new file mode 100644 index 00000000000..cfd5454d662 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTestWithoutProperties.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.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor; +import org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * {@link DubboAutoConfiguration} Test + * + * @see DubboAutoConfiguration + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CompatibleDubboAutoConfigurationTestWithoutProperties.class) +@EnableAutoConfiguration +public class CompatibleDubboAutoConfigurationTestWithoutProperties { + + @Autowired(required = false) + private ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor; + + @Autowired + private ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor; + + @Before + public void init() { + ApplicationModel.reset(); + } + + @After + public void destroy() { + ApplicationModel.reset(); + } + + @Test + public void testBeans() { + Assert.assertNull(serviceAnnotationBeanPostProcessor); + Assert.assertNotNull(referenceAnnotationBeanPostProcessor); + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnMultipleConfigTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnMultipleConfigTest.java new file mode 100644 index 00000000000..50ea1edfe70 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnMultipleConfigTest.java @@ -0,0 +1,277 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ConsumerConfig; +import org.apache.dubbo.config.ModuleConfig; +import org.apache.dubbo.config.MonitorConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ProviderConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors; + +/** + * {@link DubboAutoConfiguration} Test On multiple Dubbo Configuration + * + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = { + "dubbo.applications.application1.NAME = dubbo-demo-application", + "dubbo.modules.module1.name = dubbo-demo-module", + "dubbo.registries.registry1.address = zookeeper://192.168.99.100:32770", + "dubbo.protocols.protocol1.name=dubbo", + "dubbo.protocols.protocol1.pORt=20880", + "dubbo.monitors.monitor1.Address=zookeeper://127.0.0.1:32770", + "dubbo.providers.provider1.host=127.0.0.1", + "dubbo.consumers.consumer1.client=netty", + "dubbo.config.multiple=true", + "dubbo.scan.basePackages=org.apache.dubbo.spring.boot.dubbo, org.apache.dubbo.spring.boot.condition" + } +) +@SpringBootTest( + classes = { + DubboAutoConfigurationOnMultipleConfigTest.class + } +) +@EnableAutoConfiguration +public class DubboAutoConfigurationOnMultipleConfigTest { + + @Autowired + private Environment environment; + + @Autowired + private ApplicationContext applicationContext; + + /** + * {@link ApplicationConfig} + */ + @Autowired + @Qualifier("application1") + private ApplicationConfig application; + + /** + * {@link ModuleConfig} + */ + @Autowired + @Qualifier("module1") + private ModuleConfig module; + + /** + * {@link RegistryConfig} + */ + @Autowired + @Qualifier("registry1") + private RegistryConfig registry; + + /** + * {@link ProtocolConfig} + */ + @Autowired + @Qualifier("protocol1") + private ProtocolConfig protocol; + + /** + * {@link MonitorConfig} + */ + @Autowired + @Qualifier("monitor1") + private MonitorConfig monitor; + + /** + * {@link ProviderConfig} + */ + @Autowired + @Qualifier("provider1") + private ProviderConfig provider; + + /** + * {@link ConsumerConfig} + */ + @Autowired + @Qualifier("consumer1") + private ConsumerConfig consumer; + + @Before + public void init() { + ApplicationModel.reset(); + } + + @After + public void destroy() { + ApplicationModel.reset(); + } + + @Autowired + private Map applications = new LinkedHashMap<>(); + + @Autowired + private Map modules = new LinkedHashMap<>(); + + @Autowired + private Map registries = new LinkedHashMap<>(); + + @Autowired + private Map protocols = new LinkedHashMap<>(); + + @Autowired + private Map monitors = new LinkedHashMap<>(); + + @Autowired + private Map providers = new LinkedHashMap<>(); + + @Autowired + private Map consumers = new LinkedHashMap<>(); + + @Test + public void testMultipleDubboConfigBindingProperties() { + + + Assert.assertEquals(1, applications.size()); + + Assert.assertEquals(1, modules.size()); + + Assert.assertEquals(1, registries.size()); + + Assert.assertEquals(1, protocols.size()); + + Assert.assertEquals(1, monitors.size()); + + Assert.assertEquals(1, providers.size()); + + Assert.assertEquals(1, consumers.size()); + + } + + @Test + public void testApplicationContext() { + + /** + * Multiple {@link ApplicationConfig} + */ + Map applications = beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class); + + Assert.assertEquals(1, applications.size()); + + /** + * Multiple {@link ModuleConfig} + */ + Map modules = beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class); + + Assert.assertEquals(1, modules.size()); + + /** + * Multiple {@link RegistryConfig} + */ + Map registries = beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class); + + Assert.assertEquals(1, registries.size()); + + /** + * Multiple {@link ProtocolConfig} + */ + Map protocols = beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class); + + Assert.assertEquals(1, protocols.size()); + + /** + * Multiple {@link MonitorConfig} + */ + Map monitors = beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class); + + Assert.assertEquals(1, monitors.size()); + + /** + * Multiple {@link ProviderConfig} + */ + Map providers = beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class); + + Assert.assertEquals(1, providers.size()); + + /** + * Multiple {@link ConsumerConfig} + */ + Map consumers = beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class); + + Assert.assertEquals(1, consumers.size()); + + } + + @Test + public void testApplicationConfig() { + + Assert.assertEquals("dubbo-demo-application", application.getName()); + + } + + @Test + public void testModuleConfig() { + + Assert.assertEquals("dubbo-demo-module", module.getName()); + + } + + @Test + public void testRegistryConfig() { + + Assert.assertEquals("zookeeper://192.168.99.100:32770", registry.getAddress()); + + } + + @Test + public void testMonitorConfig() { + + Assert.assertEquals("zookeeper://127.0.0.1:32770", monitor.getAddress()); + + } + + @Test + public void testProtocolConfig() { + + Assert.assertEquals("dubbo", protocol.getName()); + Assert.assertEquals(Integer.valueOf(20880), protocol.getPort()); + + } + + @Test + public void testConsumerConfig() { + + Assert.assertEquals("netty", consumer.getClient()); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnSingleConfigTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnSingleConfigTest.java new file mode 100644 index 00000000000..765ee9039dc --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnSingleConfigTest.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.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ConsumerConfig; +import org.apache.dubbo.config.ModuleConfig; +import org.apache.dubbo.config.MonitorConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ProviderConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * {@link DubboAutoConfiguration} Test On single Dubbo Configuration + * + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = { + "dubbo.application.name = dubbo-demo-application", + "dubbo.module.name = dubbo-demo-module", + "dubbo.registry.address = zookeeper://192.168.99.100:32770", + "dubbo.protocol.name=dubbo", + "dubbo.protocol.port=20880", + "dubbo.monitor.address=zookeeper://127.0.0.1:32770", + "dubbo.provider.host=127.0.0.1", + "dubbo.consumer.client=netty" + } +) +@SpringBootTest( + classes = {DubboAutoConfigurationOnSingleConfigTest.class} +) +@EnableAutoConfiguration +public class DubboAutoConfigurationOnSingleConfigTest { + + @Autowired + private ApplicationConfig applicationConfig; + + @Autowired + private ModuleConfig moduleConfig; + + @Autowired + private RegistryConfig registryConfig; + + @Autowired + private MonitorConfig monitorConfig; + + @Autowired + private ProviderConfig providerConfig; + + @Autowired + private ConsumerConfig consumerConfig; + + @Autowired + private ProtocolConfig protocolConfig; + + @Autowired + private Environment environment; + + @Autowired + private ApplicationContext applicationContext; + + @Before + public void init() { + ApplicationModel.reset(); + } + + @After + public void destroy() { + ApplicationModel.reset(); + } + + @Test + public void testApplicationConfig() { + + Assert.assertEquals("dubbo-demo-application", applicationConfig.getName()); + + } + + @Test + public void testModuleConfig() { + + Assert.assertEquals("dubbo-demo-module", moduleConfig.getName()); + + } + + @Test + public void testRegistryConfig() { + + Assert.assertEquals("zookeeper://192.168.99.100:32770", registryConfig.getAddress()); + + } + + @Test + public void testMonitorConfig() { + + Assert.assertEquals("zookeeper://127.0.0.1:32770", monitorConfig.getAddress()); + + } + + @Test + public void testProtocolConfig() { + + Assert.assertEquals("dubbo", protocolConfig.getName()); + Assert.assertEquals(Integer.valueOf(20880), protocolConfig.getPort()); + + } + + @Test + public void testProviderConfig() { + + Assert.assertEquals("127.0.0.1", providerConfig.getHost()); + + } + + @Test + public void testConsumerConfig() { + + Assert.assertEquals("netty", consumerConfig.getClient()); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinderTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinderTest.java new file mode 100644 index 00000000000..5e0e32377ac --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinderTest.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.autoconfigure; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import com.alibaba.spring.context.config.ConfigurationBeanBinder; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Map; + +import static com.alibaba.spring.util.PropertySourcesUtils.getSubProperties; + +/** + * {@link RelaxedDubboConfigBinder} Test + */ +@RunWith(SpringRunner.class) +@TestPropertySource(properties = { + "dubbo.application.NAME=hello", + "dubbo.application.owneR=world", + "dubbo.registry.Address=10.20.153.17", + "dubbo.protocol.pORt=20881", + "dubbo.service.invoke.timeout=2000", +}) +@ContextConfiguration(classes = RelaxedDubboConfigBinder.class) +public class RelaxedDubboConfigBinderTest { + + @Autowired + private ConfigurationBeanBinder dubboConfigBinder; + + @Autowired + private ConfigurableEnvironment environment; + + @Before + public void init() { + ApplicationModel.reset(); + } + + @After + public void destroy() { + ApplicationModel.reset(); + } + + @Test + public void testBinder() { + + ApplicationConfig applicationConfig = new ApplicationConfig(); + Map properties = getSubProperties(environment.getPropertySources(), "dubbo.application"); + dubboConfigBinder.bind(properties, true, true, applicationConfig); + Assert.assertEquals("hello", applicationConfig.getName()); + Assert.assertEquals("world", applicationConfig.getOwner()); + + RegistryConfig registryConfig = new RegistryConfig(); + properties = getSubProperties(environment.getPropertySources(), "dubbo.registry"); + dubboConfigBinder.bind(properties, true, true, registryConfig); + Assert.assertEquals("10.20.153.17", registryConfig.getAddress()); + + ProtocolConfig protocolConfig = new ProtocolConfig(); + properties = getSubProperties(environment.getPropertySources(), "dubbo.protocol"); + dubboConfigBinder.bind(properties, true, true, protocolConfig); + Assert.assertEquals(Integer.valueOf(20881), protocolConfig.getPort()); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListenerTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListenerTest.java new file mode 100644 index 00000000000..1931b72d910 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListenerTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.context.event; + +import org.apache.dubbo.common.lang.ShutdownHookCallbacks; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.builder.SpringApplicationBuilder; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * {@link AwaitingNonWebApplicationListener} Test + */ +public class AwaitingNonWebApplicationListenerTest { + + @Before + public void before() { + ApplicationModel.reset(); + } + + @After + public void after() { + ApplicationModel.reset(); + } + + @Test + public void init() { + AtomicBoolean awaited = AwaitingNonWebApplicationListener.getAwaited(); + awaited.set(false); + } + + @Test + public void testSingleContextNonWebApplication() { + new SpringApplicationBuilder(Object.class) + .web(false) + .run() + .close(); + + ShutdownHookCallbacks.INSTANCE.addCallback(() -> { + AtomicBoolean awaited = AwaitingNonWebApplicationListener.getAwaited(); + Assert.assertTrue(awaited.get()); + System.out.println("Callback..."); + }); + } +// +// @Test +// public void testMultipleContextNonWebApplication() { +// new SpringApplicationBuilder(Object.class) +// .parent(Object.class) +// .web(false) +// .run().close(); +// AtomicBoolean awaited = AwaitingNonWebApplicationListener.getAwaited(); +// Assert.assertFalse(awaited.get()); +// } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListenerTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListenerTest.java new file mode 100644 index 00000000000..37273500eec --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListenerTest.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.dubbo.spring.boot.context.event; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.PropertySource; + +import java.util.Map; + + +/** + * {@link DubboConfigBeanDefinitionConflictApplicationListener} Test + * + * @since 2.7.5 + */ +public class DubboConfigBeanDefinitionConflictApplicationListenerTest { + + private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + @Before + public void init() { + ApplicationModel.reset(); + context.addApplicationListener(new DubboConfigBeanDefinitionConflictApplicationListener()); + } + + @After + public void destroy() { + context.close(); + ApplicationModel.reset(); + + } + + @Test + public void testNormalCase() { + + System.setProperty("dubbo.application.name", "test-dubbo-application"); + + context.register(DubboConfig.class); + + context.refresh(); + + ApplicationConfig applicationConfig = context.getBean(ApplicationConfig.class); + + Assert.assertEquals("test-dubbo-application", applicationConfig.getName()); + } + + @Test + public void testDuplicatedConfigsCase() { + + context.register(PropertySourceConfig.class, DubboConfig.class); + + context.register(XmlConfig.class); + + context.refresh(); + + Map beansMap = context.getBeansOfType(ApplicationConfig.class); + + ApplicationConfig applicationConfig = beansMap.get("dubbo-consumer-2.7.x"); + + Assert.assertEquals(1, beansMap.size()); + + Assert.assertEquals("dubbo-consumer-2.7.x", applicationConfig.getName()); + } + + @Test(expected = IllegalStateException.class) + public void testFailedCase() { + context.register(ApplicationConfig.class); + testDuplicatedConfigsCase(); + } + + @EnableDubboConfig + static class DubboConfig { + + } + + @PropertySource("classpath:/META-INF/dubbo.properties") + static class PropertySourceConfig { + + } + + @ImportResource("classpath:/META-INF/spring/dubbo-context.xml") + static class XmlConfig { + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerDisableTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerDisableTest.java new file mode 100644 index 00000000000..cd306044bcb --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerDisableTest.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.dubbo.spring.boot.context.event; + +import org.apache.dubbo.common.utils.ConfigUtils; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Properties; + +/** + * {@link OverrideDubboConfigApplicationListener} Test + * + * @see OverrideDubboConfigApplicationListener + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = { + "dubbo.config.override = false", + "dubbo.application.name = dubbo-demo-application", + "dubbo.module.name = dubbo-demo-module", + } +) +@SpringBootTest( + classes = {OverrideDubboConfigApplicationListener.class} +) +public class OverrideDubboConfigApplicationListenerDisableTest { + + @Before + public void init() { + ConfigUtils.getProperties().clear(); + ApplicationModel.reset(); + } + + @After + public void destroy() { + ApplicationModel.reset(); + } + + @Test + public void testOnApplicationEvent() { + + Properties properties = ConfigUtils.getProperties(); + + Assert.assertTrue(properties.isEmpty()); + Assert.assertNull(properties.get("dubbo.application.name")); + Assert.assertNull(properties.get("dubbo.module.name")); + + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerTest.java new file mode 100644 index 00000000000..f0371480395 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.context.event; + +import org.apache.dubbo.common.utils.ConfigUtils; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Properties; + +/** + * {@link OverrideDubboConfigApplicationListener} Test + * + * @see OverrideDubboConfigApplicationListener + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@TestPropertySource( + properties = { + "dubbo.application.name = dubbo-demo-application", + "dubbo.module.name = dubbo-demo-module", + "dubbo.registry.address = zookeeper://192.168.99.100:32770" + } +) +@SpringBootTest( + classes = {OverrideDubboConfigApplicationListener.class} +) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +public class OverrideDubboConfigApplicationListenerTest { + + @BeforeClass + public static void init() { + ApplicationModel.reset(); + ConfigUtils.getProperties().clear(); + } + + @AfterClass + public static void destroy() { + ApplicationModel.reset(); + } + + @Test + public void testOnApplicationEvent() { + Properties properties = ConfigUtils.getProperties(); + + Assert.assertEquals("dubbo-demo-application", properties.get("dubbo.application.name")); + Assert.assertEquals("dubbo-demo-module", properties.get("dubbo.module.name")); + Assert.assertEquals("zookeeper://192.168.99.100:32770", properties.get("dubbo.registry.address")); + + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListenerTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListenerTest.java new file mode 100644 index 00000000000..dd8dde8531b --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListenerTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.context.event; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * {@link WelcomeLogoApplicationListener} Test + * + * @see WelcomeLogoApplicationListener + * @since 2.7.0 + */ +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = {WelcomeLogoApplicationListener.class} +) +public class WelcomeLogoApplicationListenerTest { + + @Autowired + private WelcomeLogoApplicationListener welcomeLogoApplicationListener; + + @Test + public void testOnApplicationEvent() { + + Assert.assertNotNull(welcomeLogoApplicationListener.buildBannerText()); + + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java new file mode 100644 index 00000000000..860bc2e8a34 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.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.dubbo.spring.boot.env; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.core.Ordered; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.mock.env.MockEnvironment; + +import java.util.HashMap; + +/** + * {@link DubboDefaultPropertiesEnvironmentPostProcessor} Test + */ +public class DubboDefaultPropertiesEnvironmentPostProcessorTest { + + private DubboDefaultPropertiesEnvironmentPostProcessor instance = + new DubboDefaultPropertiesEnvironmentPostProcessor(); + + private SpringApplication springApplication = new SpringApplication(); + + @Test + public void testOrder() { + Assert.assertEquals(Ordered.LOWEST_PRECEDENCE, instance.getOrder()); + } + + @Test + public void testPostProcessEnvironment() { + MockEnvironment environment = new MockEnvironment(); + // Case 1 : Not Any property + instance.postProcessEnvironment(environment, springApplication); + // Get PropertySources + MutablePropertySources propertySources = environment.getPropertySources(); + // Nothing to change + PropertySource defaultPropertySource = propertySources.get("defaultProperties"); + Assert.assertNotNull(defaultPropertySource); + Assert.assertEquals("true", defaultPropertySource.getProperty("dubbo.config.multiple")); + Assert.assertEquals("false", defaultPropertySource.getProperty("dubbo.application.qos-enable")); + + // Case 2 : Only set property "spring.application.name" + environment.setProperty("spring.application.name", "demo-dubbo-application"); + instance.postProcessEnvironment(environment, springApplication); + defaultPropertySource = propertySources.get("defaultProperties"); + Object dubboApplicationName = defaultPropertySource.getProperty("dubbo.application.name"); + Assert.assertEquals("demo-dubbo-application", dubboApplicationName); + + // Case 3 : Only set property "dubbo.application.name" + // Rest environment + environment = new MockEnvironment(); + propertySources = environment.getPropertySources(); + environment.setProperty("dubbo.application.name", "demo-dubbo-application"); + instance.postProcessEnvironment(environment, springApplication); + defaultPropertySource = propertySources.get("defaultProperties"); + Assert.assertNotNull(defaultPropertySource); + dubboApplicationName = environment.getProperty("dubbo.application.name"); + Assert.assertEquals("demo-dubbo-application", dubboApplicationName); + + // Case 4 : If "defaultProperties" PropertySource is present in PropertySources + // Rest environment + environment = new MockEnvironment(); + propertySources = environment.getPropertySources(); + propertySources.addLast(new MapPropertySource("defaultProperties", new HashMap())); + environment.setProperty("spring.application.name", "demo-dubbo-application"); + instance.postProcessEnvironment(environment, springApplication); + defaultPropertySource = propertySources.get("defaultProperties"); + dubboApplicationName = defaultPropertySource.getProperty("dubbo.application.name"); + Assert.assertEquals("demo-dubbo-application", dubboApplicationName); + + // Case 5 : Rest dubbo.config.multiple and dubbo.application.qos-enable + environment = new MockEnvironment(); + propertySources = environment.getPropertySources(); + propertySources.addLast(new MapPropertySource("defaultProperties", new HashMap())); + environment.setProperty("dubbo.config.multiple", "false"); + environment.setProperty("dubbo.application.qos-enable", "true"); + instance.postProcessEnvironment(environment, springApplication); + Assert.assertEquals("false", environment.getProperty("dubbo.config.multiple")); + Assert.assertEquals("true", environment.getProperty("dubbo.application.qos-enable")); + } +} \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/DubboUtilsTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/DubboUtilsTest.java new file mode 100644 index 00000000000..31928678aee --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/DubboUtilsTest.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.dubbo.spring.boot.util; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.mock.env.MockEnvironment; + +import java.util.SortedMap; + +import static org.apache.dubbo.spring.boot.util.DubboUtils.BASE_PACKAGES_PROPERTY_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DEFAULT_MULTIPLE_CONFIG_PROPERTY_VALUE; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_APPLICATION_ID_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_APPLICATION_NAME_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_APPLICATION_QOS_ENABLE_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_CONFIG_MULTIPLE_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_CONFIG_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_GITHUB_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_MAILING_LIST; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SCAN_PREFIX; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SPRING_BOOT_GITHUB_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SPRING_BOOT_GIT_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SPRING_BOOT_ISSUES_URL; +import static org.apache.dubbo.spring.boot.util.DubboUtils.MULTIPLE_CONFIG_PROPERTY_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.OVERRIDE_CONFIG_FULL_PROPERTY_NAME; +import static org.apache.dubbo.spring.boot.util.DubboUtils.SPRING_APPLICATION_NAME_PROPERTY; +import static org.apache.dubbo.spring.boot.util.DubboUtils.filterDubboProperties; + +/** + * {@link DubboUtils} Test + * + * @see DubboUtils + * @since 2.7.0 + */ +public class DubboUtilsTest { + + @Test + public void testConstants() { + + Assert.assertEquals("dubbo", DUBBO_PREFIX); + + Assert.assertEquals("dubbo.scan.", DUBBO_SCAN_PREFIX); + + Assert.assertEquals("base-packages", BASE_PACKAGES_PROPERTY_NAME); + + Assert.assertEquals("dubbo.config.", DUBBO_CONFIG_PREFIX); + + Assert.assertEquals("multiple", MULTIPLE_CONFIG_PROPERTY_NAME); + + Assert.assertEquals("dubbo.config.override", OVERRIDE_CONFIG_FULL_PROPERTY_NAME); + + Assert.assertEquals("https://github.com/apache/dubbo-spring-boot-project", DUBBO_SPRING_BOOT_GITHUB_URL); + Assert.assertEquals("https://github.com/apache/dubbo-spring-boot-project.git", DUBBO_SPRING_BOOT_GIT_URL); + Assert.assertEquals("https://github.com/apache/dubbo-spring-boot-project/issues", DUBBO_SPRING_BOOT_ISSUES_URL); + + Assert.assertEquals("https://github.com/apache/dubbo", DUBBO_GITHUB_URL); + + Assert.assertEquals("dev@dubbo.apache.org", DUBBO_MAILING_LIST); + + Assert.assertEquals("spring.application.name", SPRING_APPLICATION_NAME_PROPERTY); + Assert.assertEquals("dubbo.application.id", DUBBO_APPLICATION_ID_PROPERTY); + Assert.assertEquals("dubbo.application.name", DUBBO_APPLICATION_NAME_PROPERTY); + Assert.assertEquals("dubbo.application.qos-enable", DUBBO_APPLICATION_QOS_ENABLE_PROPERTY); + Assert.assertEquals("dubbo.config.multiple", DUBBO_CONFIG_MULTIPLE_PROPERTY); + + Assert.assertTrue(DEFAULT_MULTIPLE_CONFIG_PROPERTY_VALUE); + + Assert.assertTrue(DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE); + + + } + + + @Test + public void testFilterDubboProperties() { + + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("message", "Hello,World"); + environment.setProperty(DUBBO_CONFIG_PREFIX + MULTIPLE_CONFIG_PROPERTY_NAME, "true"); + environment.setProperty(OVERRIDE_CONFIG_FULL_PROPERTY_NAME, "true"); + + SortedMap dubboProperties = filterDubboProperties(environment); + + Assert.assertEquals("true", dubboProperties.get(DUBBO_CONFIG_PREFIX + MULTIPLE_CONFIG_PROPERTY_NAME)); + Assert.assertEquals("true", dubboProperties.get(OVERRIDE_CONFIG_FULL_PROPERTY_NAME)); + + } + +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.java new file mode 100644 index 00000000000..b70f35145cd --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.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.dubbo.spring.boot.util; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.StandardEnvironment; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@link EnvironmentUtils} Test + * + * @see EnvironmentUtils + * @since 2.7.0 + */ +public class EnvironmentUtilsTest { + + @Test + public void testExtraProperties() { + + System.setProperty("user.name", "mercyblitz"); + + StandardEnvironment environment = new StandardEnvironment(); + + Map map = new HashMap<>(); + + map.put("user.name", "Mercy"); + + MapPropertySource propertySource = new MapPropertySource("first", map); + + CompositePropertySource compositePropertySource = new CompositePropertySource("comp"); + + compositePropertySource.addFirstPropertySource(propertySource); + + MutablePropertySources propertySources = environment.getPropertySources(); + + propertySources.addFirst(compositePropertySource); + + Map properties = EnvironmentUtils.extractProperties(environment); + + Assert.assertEquals("Mercy", properties.get("user.name")); + + } +} diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/dubbo.properties b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/dubbo.properties new file mode 100644 index 00000000000..97633c54d23 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/dubbo.properties @@ -0,0 +1 @@ +dubbo.application.id = test-dubbo-application-id \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/spring/dubbo-context.xml b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/spring/dubbo-context.xml new file mode 100644 index 00000000000..268cf907fee --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/spring/dubbo-context.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-compatible/pom.xml new file mode 100644 index 00000000000..06f277c356d --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/pom.xml @@ -0,0 +1,53 @@ + + + + + org.apache.dubbo + dubbo-spring-boot + ${revision} + ../pom.xml + + 4.0.0 + + dubbo-spring-boot-compatible + pom + Apache Dubbo Spring Boot Compatible for Spring Boot 1.x + + + 1.5.22.RELEASE + + + + autoconfigure + actuator + + + + + + + spring-boot-1.4 + + 1.4.7.RELEASE + + + + + \ No newline at end of file diff --git a/dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml new file mode 100644 index 00000000000..d3816464f2e --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml @@ -0,0 +1,49 @@ + + + + + org.apache.dubbo + dubbo-spring-boot + ${revision} + ../pom.xml + + 4.0.0 + + dubbo-spring-boot-starter + jar + Apache Dubbo Spring Boot Starter + + + + + + org.springframework.boot + spring-boot-starter + true + + + + org.apache.dubbo + dubbo-spring-boot-autoconfigure + ${revision} + + + + \ No newline at end of file diff --git a/dubbo-spring-boot/pom.xml b/dubbo-spring-boot/pom.xml new file mode 100644 index 00000000000..2299bb68479 --- /dev/null +++ b/dubbo-spring-boot/pom.xml @@ -0,0 +1,193 @@ + + + + 4.0.0 + + org.apache.dubbo + dubbo-parent + ${revision} + ../pom.xml + + + dubbo-spring-boot + + pom + Apache Dubbo Spring Boot Parent + + + dubbo-spring-boot-actuator + dubbo-spring-boot-autoconfigure + dubbo-spring-boot-compatible + dubbo-spring-boot-starter + + + + 2.3.1.RELEASE + ${revision} + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + spring-milestone + Spring Milestone + https://repo.spring.io/milestone + + false + + + + spring-snapshot + Spring Snapshot + https://repo.spring.io/snapshot + + true + + + + rabbit-milestone + Rabbit Milestone + https://dl.bintray.com/rabbitmq/maven-milestones + + false + + + + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + spring-milestone + Spring Milestone + https://repo.spring.io/milestone + + false + + + + spring-snapshot + Spring Snapshot + https://repo.spring.io/snapshot + + true + + + + + + + + + spring-boot-2.0 + + 2.0.9.RELEASE + + + + + + spring-boot-2.1 + + 2.1.15.RELEASE + + + + + + spring-boot-2.2 + + 2.2.8.RELEASE + + + + + + + + + src/main/resources/ + false + + + ../ + META-INF/ + false + + NOTICE + LICENSE + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven_jar_version} + + + true + true + + true + true + + + ${project.version} + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven_compiler_version} + + + -parameters + + true + ${java_source_version} + ${java_target_version} + ${file_encoding} + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index aaf82dab31d..f3998a95165 100644 --- a/pom.xml +++ b/pom.xml @@ -88,14 +88,14 @@ - 5.6.0 + 5.7.1 3.11.1 - 1.3 + 2.2 5.2.4.Final 2.2.4 2.2.7 2.2 - 2.23.4 + 3.8.0 -server -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m @@ -112,21 +112,21 @@ UTF-8 3.2.0 - 2.22.1 + 2.22.2 2.8.2 - 3.6.0 + 3.8.1 3.2.1 - 3.0.1 - 9.4.11.v20180605 - 3.0.0 - 0.8.2 - 1.1.0 - 3.0.0-M2 - 0.12 + 3.2.0 + 9.4.38.v20210224 + 3.1.2 + 0.8.6 + 1.2.5 + 3.0.0-M3 + 0.13 true true - 2.7.9-SNAPSHOT + 2.7.11-SNAPSHOT @@ -151,6 +151,7 @@ dubbo-dependencies dubbo-metadata dubbo-build-tools + dubbo-spring-boot @@ -180,7 +181,7 @@ org.hamcrest - hamcrest-all + hamcrest ${hamcrest_version} test @@ -252,7 +253,7 @@ com.puppycrawl.tools checkstyle - 8.9 + 8.41 org.apache.dubbo @@ -395,7 +396,7 @@ org.codehaus.mojo license-maven-plugin - 1.20 + 2.0.0 license-check @@ -639,11 +640,11 @@ maven-failsafe-plugin - 2.22.1 + 2.22.2 maven-clean-plugin - 3.0.0 + 3.1.0 org.ops4j.pax.exam @@ -652,7 +653,7 @@ maven-dependency-plugin - 2.10 + 3.1.2 @@ -662,12 +663,12 @@ org.codehaus.mojo build-helper-maven-plugin - 1.10 + 3.2.0 org.fusesource.hawtjni maven-hawtjni-plugin - 1.14 + 1.15 kr.motd.maven @@ -677,7 +678,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.2 + 3.2.4 false