From be54ec206a06824eb61af7349d10b156c6dddc5e Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Thu, 1 Apr 2021 15:28:31 +0800 Subject: [PATCH] [Spring Boot Project] Merge 3.0.x Spring-Boot project into Dubbo 3.0.x (#7441) * [Spring Boot Project] Merge 3.0.x Spring-Boot project into Dubbo 3.0.x * add junit5 dependency * ignore ut * change to junit5 * reset ApplicationModel --- .github/workflows/build-and-test-3.yml | 9 - dubbo-distribution/dubbo-bom/pom.xml | 26 + .../JMX_HealthEndpoint.png | Bin 0 -> 230227 bytes .../dubbo-spring-boot-actuator/README.md | 499 ++++++++++++++++++ .../dubbo-spring-boot-actuator/pom.xml | 124 +++++ ...boEndpointAnnotationAutoConfiguration.java | 88 +++ .../DubboConfigsMetadataEndpoint.java | 43 ++ .../endpoint/DubboMetadataEndpoint.java | 44 ++ .../DubboPropertiesMetadataEndpoint.java | 43 ++ .../DubboReferencesMetadataEndpoint.java | 44 ++ .../DubboServicesMetadataEndpoint.java | 44 ++ .../endpoint/DubboShutdownEndpoint.java | 44 ++ ...ompatibleConditionalOnEnabledEndpoint.java | 51 ++ .../CompatibleOnEnabledEndpointCondition.java | 69 +++ .../dubbo-endpoints-default.properties | 21 + .../main/resources/META-INF/spring.factories | 2 + ...dpointAnnotationAutoConfigurationTest.java | 264 +++++++++ .../actuate/endpoint/DubboEndpointTest.java | 93 ++++ .../dubbo-spring-boot-autoconfigure/README.md | 225 ++++++++ .../config-popup-window.png | Bin 0 -> 85096 bytes .../mconfig-popup-window.png | Bin 0 -> 60869 bytes .../dubbo-spring-boot-autoconfigure/pom.xml | 85 +++ .../BinderDubboConfigBinder.java | 79 +++ ...DubboRelaxedBinding2AutoConfiguration.java | 92 ++++ .../main/resources/META-INF/spring.factories | 2 + .../BinderDubboConfigBinderTest.java | 72 +++ ...oRelaxedBinding2AutoConfigurationTest.java | 94 ++++ ...ropertiesEnvironmentPostProcessorTest.java | 97 ++++ .../boot/util/EnvironmentUtilsTest.java | 63 +++ .../src/test/resources/dubbo.properties | 5 + .../actuator/pom.xml | 94 ++++ .../DubboEndpointAutoConfiguration.java | 55 ++ ...ubboEndpointMetadataAutoConfiguration.java | 39 ++ ...DubboHealthIndicatorAutoConfiguration.java | 50 ++ ...ndpointManagementContextConfiguration.java | 49 ++ .../boot/actuate/endpoint/DubboEndpoint.java | 49 ++ .../metadata/AbstractDubboMetadata.java | 124 +++++ .../metadata/DubboConfigsMetadata.java | 87 +++ .../endpoint/metadata/DubboMetadata.java | 62 +++ .../metadata/DubboPropertiesMetadata.java | 36 ++ .../metadata/DubboReferencesMetadata.java | 79 +++ .../metadata/DubboServicesMetadata.java | 84 +++ .../metadata/DubboShutdownMetadata.java | 78 +++ .../endpoint/mvc/DubboMvcEndpoint.java | 112 ++++ .../actuate/health/DubboHealthIndicator.java | 211 ++++++++ .../DubboHealthIndicatorProperties.java | 99 ++++ .../main/resources/META-INF/spring.factories | 6 + .../DubboEndpointAutoConfigurationTest.java | 238 +++++++++ .../health/DubboHealthIndicatorTest.java | 89 ++++ .../autoconfigure/pom.xml | 88 +++ .../autoconfigure/DubboAutoConfiguration.java | 112 ++++ .../DubboConfigurationProperties.java | 300 +++++++++++ .../DubboRelaxedBindingAutoConfiguration.java | 73 +++ .../RelaxedDubboConfigBinder.java | 48 ++ .../ServiceBeanIdConflictProcessor.java | 115 ++++ .../DubboApplicationContextInitializer.java | 50 ++ .../AwaitingNonWebApplicationListener.java | 199 +++++++ ...DefinitionConflictApplicationListener.java | 121 +++++ ...verrideDubboConfigApplicationListener.java | 79 +++ .../event/WelcomeLogoApplicationListener.java | 94 ++++ ...ultPropertiesEnvironmentPostProcessor.java | 136 +++++ .../dubbo/spring/boot/util/DubboUtils.java | 215 ++++++++ .../spring/boot/util/EnvironmentUtils.java | 114 ++++ .../main/resources/META-INF/spring.factories | 12 + .../apache/dubbo/spring/boot/TestSuite.java | 53 ++ .../CompatibleDubboAutoConfigurationTest.java | 59 +++ ...utoConfigurationTestWithoutProperties.java | 64 +++ ...AutoConfigurationOnMultipleConfigTest.java | 277 ++++++++++ ...boAutoConfigurationOnSingleConfigTest.java | 151 ++++++ .../RelaxedDubboConfigBinderTest.java | 90 ++++ ...AwaitingNonWebApplicationListenerTest.java | 75 +++ ...nitionConflictApplicationListenerTest.java | 107 ++++ ...oConfigApplicationListenerDisableTest.java | 74 +++ ...ideDubboConfigApplicationListenerTest.java | 75 +++ .../WelcomeLogoApplicationListenerTest.java | 48 ++ ...ropertiesEnvironmentPostProcessorTest.java | 97 ++++ .../spring/boot/util/DubboUtilsTest.java | 105 ++++ .../boot/util/EnvironmentUtilsTest.java | 63 +++ .../test/resources/META-INF/dubbo.properties | 1 + .../META-INF/spring/dubbo-context.xml | 14 + .../dubbo-spring-boot-compatible/pom.xml | 53 ++ .../dubbo-spring-boot-starter/pom.xml | 49 ++ dubbo-spring-boot/pom.xml | 193 +++++++ pom.xml | 1 + 84 files changed, 7359 insertions(+), 9 deletions(-) create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/JMX_HealthEndpoint.png create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/README.md create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboConfigsMetadataEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboMetadataEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboPropertiesMetadataEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboReferencesMetadataEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboServicesMetadataEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboShutdownEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleConditionalOnEnabledEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/condition/CompatibleOnEnabledEndpointCondition.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/dubbo-endpoints-default.properties create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/main/resources/META-INF/spring.factories create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAnnotationAutoConfigurationTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpointTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/README.md create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/config-popup-window.png create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/mconfig-popup-window.png create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinder.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/BinderDubboConfigBinderTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBinding2AutoConfigurationTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/test/resources/dubbo.properties create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/pom.xml create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointMetadataAutoConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboHealthIndicatorAutoConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboMvcEndpointManagementContextConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/DubboEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/AbstractDubboMetadata.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboConfigsMetadata.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboMetadata.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboPropertiesMetadata.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboReferencesMetadata.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboServicesMetadata.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/mvc/DubboMvcEndpoint.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicator.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorProperties.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/resources/META-INF/spring.factories create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/autoconfigure/DubboEndpointAutoConfigurationTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/test/java/org/apache/dubbo/spring/boot/actuate/health/DubboHealthIndicatorTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/pom.xml create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboConfigurationProperties.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboRelaxedBindingAutoConfiguration.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinder.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/beans/factory/config/ServiceBeanIdConflictProcessor.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/DubboApplicationContextInitializer.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListener.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListener.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListener.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessor.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/DubboUtils.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/util/EnvironmentUtils.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/resources/META-INF/spring.factories create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/TestSuite.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/CompatibleDubboAutoConfigurationTestWithoutProperties.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnMultipleConfigTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/DubboAutoConfigurationOnSingleConfigTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/autoconfigure/RelaxedDubboConfigBinderTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListenerTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/DubboConfigBeanDefinitionConflictApplicationListenerTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerDisableTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/OverrideDubboConfigApplicationListenerTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/WelcomeLogoApplicationListenerTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/env/DubboDefaultPropertiesEnvironmentPostProcessorTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/DubboUtilsTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/util/EnvironmentUtilsTest.java create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/dubbo.properties create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/test/resources/META-INF/spring/dubbo-context.xml create mode 100644 dubbo-spring-boot/dubbo-spring-boot-compatible/pom.xml create mode 100644 dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml create mode 100644 dubbo-spring-boot/pom.xml diff --git a/.github/workflows/build-and-test-3.yml b/.github/workflows/build-and-test-3.yml index b1e6302ddc..4cfa652b0e 100644 --- a/.github/workflows/build-and-test-3.yml +++ b/.github/workflows/build-and-test-3.yml @@ -24,11 +24,6 @@ jobs: - uses: actions/checkout@v2 with: path: dubbo - - uses: actions/checkout@v2 - with: - repository: 'apache/dubbo-spring-boot-project' - ref: ${{env.DUBBO_SPRING_BOOT_REF}} - path: dubbo-spring-boot-project - uses: actions/setup-java@v1 with: java-version: 8 @@ -46,10 +41,6 @@ jobs: 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 - - name: "Build Dubbo Spring Boot with Maven" - run: | - cd ./dubbo-spring-boot-project - ./mvnw --batch-mode --no-transfer-progress clean source:jar install -Dmaven.test.skip=true -Dmaven.test.skip.exec=true - name: "Calculate Dubbo Version" id: dubbo-version run: | diff --git a/dubbo-distribution/dubbo-bom/pom.xml b/dubbo-distribution/dubbo-bom/pom.xml index 1c1c30dadf..b4ff9b8000 100644 --- a/dubbo-distribution/dubbo-bom/pom.xml +++ b/dubbo-distribution/dubbo-bom/pom.xml @@ -232,6 +232,32 @@ dubbo-configcenter-nacos ${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-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 0000000000000000000000000000000000000000..18e9f92abb1b08f1b7f4f7b72806cccd143e38ea GIT binary patch literal 230227 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%0DdF>k?M@PL2;*xEuJn* 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

G#XfP}MZ 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 literal 0 HcmV?d00001 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 0000000000..3c478d9ba6 --- /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 0000000000..c7e15bb081 --- /dev/null +++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml @@ -0,0 +1,124 @@ + + + + + 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 + + + + org.junit.platform + junit-platform-runner + 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 0000000000..7ae8b848de --- /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 0000000000..b861e5ccb0 --- /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 0000000000..edf732840f --- /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 0000000000..4295113f75 --- /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 0000000000..18fc09b273 --- /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 0000000000..552e4b8c08 --- /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 0000000000..e62b57ba1b --- /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 0000000000..245498040d --- /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 0000000000..8de2a5682d --- /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 0000000000..4df591ed79 --- /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 0000000000..5dc6c60c18 --- /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 0000000000..255ab7676c --- /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,264 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.spring.boot.actuate.autoconfigure; + +import org.apache.dubbo.common.BaseServiceMetadata; +import org.apache.dubbo.config.annotation.DubboReference; +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.Assert; +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 org.junit.jupiter.api.extension.ExtendWith; +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.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +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 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest( + classes = { + DubboEndpointAnnotationAutoConfigurationTest.class, + DubboEndpointAnnotationAutoConfigurationTest.ConsumerConfiguration.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 +@Disabled +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; + + @BeforeEach + public void init() { + ApplicationModel.reset(); + } + + @AfterEach + 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()); + String injectedField = "private " + DemoService.class.getName() + " " + ConsumerConfiguration.class.getName() + ".demoService"; + Map referenceMap = references.get(injectedField); + Assert.assertNotNull(referenceMap); + Assert.assertEquals(DemoService.class, referenceMap.get("interfaceClass")); + Assert.assertEquals(BaseServiceMetadata.buildServiceKey(DemoService.class.getName(),ConsumerConfiguration.DEMO_GROUP,ConsumerConfiguration.DEMO_VERSION), + referenceMap.get("uniqueServiceName")); + } + + @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)"; + } + + } + + @Configuration + static class ConsumerConfiguration { + public static final String DEMO_GROUP = "demo"; + public static final String DEMO_VERSION = "1.0.0"; + + @DubboReference(group = DEMO_GROUP, version = DEMO_VERSION) + private DemoService demoService; + + } +} 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 0000000000..23c8d6bc3c --- /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.Assert; +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.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Map; + +import static org.apache.dubbo.common.Version.getVersion; + +/** + * {@link DubboMetadataEndpoint} Test + * + * @see DubboMetadataEndpoint + * @since 2.7.0 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest( + classes = { + DubboMetadataEndpoint.class + }, + properties = { + "dubbo.application.name = dubbo-demo-application" + } +) +@EnableAutoConfiguration +public class DubboEndpointTest { + + + @Autowired + private DubboMetadataEndpoint dubboEndpoint; + + @BeforeEach + public void init() { + ApplicationModel.reset(); + } + + @AfterEach + 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 0000000000..b99cc47370 --- /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 0000000000000000000000000000000000000000..45246fd5314c061f7c855c544325d1177e2d4c02 GIT binary patch literal 85096 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 E7tbqJ?EnA( literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fd8a7588ce529c07b20622f6864cd3cef9ec5b4c GIT binary patch literal 60869 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 + + + + 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 0000000000..16e2dde977 --- /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 0000000000..d82747d163 --- /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 0000000000..057eb45415 --- /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 0000000000..45b8c336f0 --- /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 0000000000..adc2f33d04 --- /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 0000000000..860bc2e8a3 --- /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 0000000000..b70f35145c --- /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 0000000000..abe14a103d --- /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 0000000000..2949cd7a13 --- /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 0000000000..84091242bf --- /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 0000000000..e6dd0a4d16 --- /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 0000000000..1bcfcb0b49 --- /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 0000000000..5b5991876f --- /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 0000000000..ef0d9b9565 --- /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 0000000000..3f6c3b0742 --- /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,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.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 Meatadata + * + * @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); + if (value != null) { + 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 0000000000..081c2c1a72 --- /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 0000000000..9149ca7f9d --- /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 0000000000..f7edc04af1 --- /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 0000000000..7a590b3bad --- /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,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.actuate.endpoint.metadata; + +import org.apache.dubbo.config.ReferenceConfig; +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(); + ReferenceConfig referenceConfig = referenceBean.getReferenceConfig(); + + Map beanMetadata = null; + if (referenceConfig != null) { + beanMetadata = resolveBeanMetadata(referenceConfig); + //beanMetadata.put("invoker", resolveBeanMetadata(referenceBean.get())); + } else { + // referenceBean is not initialized + beanMetadata = new LinkedHashMap<>(); + } + + 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 0000000000..0053e2be39 --- /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 0000000000..e62b55b1bd --- /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.ReferenceConfigBase; +import org.apache.dubbo.config.spring.ServiceBean; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import org.springframework.stereotype.Component; + +import java.util.Collection; +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 + Collection> references = ApplicationModel.getConfigManager().getReferences(); + for (ReferenceConfigBase reference : references) { + reference.destroy(); + } + shutdownCountData.put("references", references.size()); + + // 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 0000000000..ac3bd1e7cf --- /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 0000000000..87d0a0fa48 --- /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 0000000000..7ce20659e6 --- /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 0000000000..db1b811c3e --- /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 0000000000..f383bc09b2 --- /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 0000000000..0c55f7f5b2 --- /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 0000000000..488bc92017 --- /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 0000000000..b209820c5b --- /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,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.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(); + dubboLifecycleComponentApplicationListener.setApplicationContext(applicationContext); + context.addApplicationListener(dubboLifecycleComponentApplicationListener); + + DubboBootstrapApplicationListener dubboBootstrapApplicationListener = new DubboBootstrapApplicationListener(); + dubboBootstrapApplicationListener.setApplicationContext(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 0000000000..f6cef20f73 --- /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 0000000000..3cefb46e98 --- /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 0000000000..fcd461a789 --- /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 0000000000..9b3ee16a1f --- /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 0000000000..ed8c96c020 --- /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 0000000000..a897a0fac1 --- /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 0000000000..f18c8b2e4e --- /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 0000000000..45d98a6a77 --- /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 0000000000..7aa0d8d3e9 --- /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 0000000000..6fb1a88145 --- /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 0000000000..fdc396de04 --- /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 0000000000..4af16edf55 --- /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 0000000000..6cf0f7f9b7 --- /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 0000000000..d6f2bb99a1 --- /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 0000000000..73c1f6f3c4 --- /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 0000000000..cfd5454d66 --- /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 0000000000..50ea1edfe7 --- /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 0000000000..765ee9039d --- /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 0000000000..5e0e32377a --- /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 0000000000..1931b72d91 --- /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 0000000000..37273500ee --- /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 0000000000..cd306044bc --- /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 0000000000..f037148039 --- /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 0000000000..dd8dde8531 --- /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 0000000000..860bc2e8a3 --- /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 0000000000..31928678ae --- /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 0000000000..b70f35145c --- /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 0000000000..97633c54d2 --- /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 0000000000..268cf907fe --- /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 0000000000..06f277c356 --- /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 0000000000..d3816464f2 --- /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 0000000000..2299bb6847 --- /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 155da8cd0c..0384c6f899 100644 --- a/pom.xml +++ b/pom.xml @@ -149,6 +149,7 @@ dubbo-dependencies dubbo-metadata dubbo-build-tools + dubbo-spring-boot