diff --git a/.github/workflows/cloud_code_scan.yml b/.github/workflows/cloud_code_scan.yml new file mode 100644 index 000000000..9a6da4aec --- /dev/null +++ b/.github/workflows/cloud_code_scan.yml @@ -0,0 +1,22 @@ +name: Alipay Cloud Devops Codescan +on: + pull_request_target: +jobs: + stc: # Code security scanning + runs-on: ubuntu-latest + steps: + - name: codeScan + uses: layotto/alipay-cloud-devops-codescan@main + with: + parent_uid: ${{ secrets.ALI_PID }} + private_key: ${{ secrets.ALI_PK }} + scan_type: stc + sca: # Open source compliance scanning + runs-on: ubuntu-latest + steps: + - name: codeScan + uses: layotto/alipay-cloud-devops-codescan@main + with: + parent_uid: ${{ secrets.ALI_PID }} + private_key: ${{ secrets.ALI_PK }} + scan_type: sca diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 09689b68f..b056b2a56 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,7 +1,7 @@ # This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven -name: CI +name: Build And Test on: push: @@ -13,15 +13,16 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: - jdk: [17] + jdk: [17, 21, 25-ea] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v5 with: + distribution: 'temurin' java-version: ${{ matrix.jdk }} - name: Install Zookeeper run: echo "Install Zookeeper 3.5.6" @@ -33,7 +34,7 @@ jobs: run: mvn clean install -B -V && sh ./tools/check_format.sh - name: Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7524cbf47..ebf7606fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,9 +11,9 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' diff --git a/README.md b/README.md index e58a18616..626139b8e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## SOFABoot -![build](https://github.com/sofastack/sofa-boot/workflows/build/badge.svg) +[![Build And Test](https://github.com/sofastack/sofa-boot/actions/workflows/maven.yml/badge.svg)](https://github.com/sofastack/sofa-boot/actions/workflows/maven.yml) [![Coverage Status](https://codecov.io/gh/sofastack/sofa-boot/branch/master/graph/badge.svg)](https://codecov.io/gh/sofastack/sofa-boot/branch/master) ![license](https://img.shields.io/badge/license-Apache--2.0-green.svg) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/sofastack/sofa-boot.svg)](http://isitmaintained.com/project/sofastack/sofa-boot "Average time to resolve an issue") @@ -83,17 +83,8 @@ SOFABoot is compiled under JDK 17 currently and needs [Apache Maven 3.5.4](https ### Community See our community [materials](https://github.com/sofastack/community/blob/master/ROLES-EN.md). -Scan the QR code below with DingTalk(钉钉) to join the SOFAStack user group. -

- -

- -Scan the QR code below with WeChat(微信) to Follow our Official Accounts. -

- -

- - +### Contact Us +See [Community contact way](https://www.sofastack.tech/awesome/#community) ## Acknowledgements The first version of SOFA is created by Felix(阿玺), lots of thanks are given to Felix for laying a solid foundation for SOFA. diff --git a/README_ZH.md b/README_ZH.md index d442ed48c..9d30ac7da 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,6 +1,6 @@ ## SOFABoot -![build](https://github.com/sofastack/sofa-boot/workflows/build/badge.svg) +[![Build And Test](https://github.com/sofastack/sofa-boot/actions/workflows/maven.yml/badge.svg)](https://github.com/sofastack/sofa-boot/actions/workflows/maven.yml) [![Coverage Status](https://codecov.io/gh/sofastack/sofa-boot/branch/master/graph/badge.svg)](https://codecov.io/gh/sofastack/sofa-boot/branch/master) ![license](https://img.shields.io/badge/license-Apache--2.0-green.svg) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/sofastack/sofa-boot.svg)](http://isitmaintained.com/project/sofastack/sofa-boot "Average time to resolve an issue") diff --git a/pom.xml b/pom.xml index d06436f11..cb455d709 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.5 + 3.5.6 com.alipay.sofa @@ -35,15 +35,15 @@ SOFABoot Build - 4.1.0 + 4.6.0 ${revision} - 3.1.5 + 3.5.6 17 UTF-8 UTF-8 - 1.6.13 + 1.7.0 1.6 1.2.7 @@ -96,7 +96,7 @@ true ossrh - https://oss.sonatype.org/ + https://ossrh-staging-api.central.sonatype.com/ false @@ -125,11 +125,11 @@ ossrh - https://oss.sonatype.org/content/repositories/snapshots + https://central.sonatype.com/repository/maven-snapshots/ ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/ diff --git a/sofa-boot-project/pom.xml b/sofa-boot-project/pom.xml index 743866ffd..1d7b20c80 100644 --- a/sofa-boot-project/pom.xml +++ b/sofa-boot-project/pom.xml @@ -9,6 +9,7 @@ ${revision} + SOFABoot Project sofa-boot-project pom diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/pom.xml b/sofa-boot-project/sofa-boot-actuator-autoconfigure/pom.xml index f3ecd8c69..21f62de55 100644 --- a/sofa-boot-project/sofa-boot-actuator-autoconfigure/pom.xml +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/pom.xml @@ -11,6 +11,7 @@ 4.0.0 + SOFABoot Actuator Autoconfigure sofa-boot-actuator-autoconfigure @@ -30,6 +31,12 @@ spring-boot-actuator-autoconfigure + + org.springframework.boot + spring-boot-configuration-processor + true + + com.alipay.sofa runtime-sofa-boot diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfiguration.java b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfiguration.java index c8a9e0d9f..4678ce909 100644 --- a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfiguration.java @@ -21,10 +21,13 @@ import com.alipay.sofa.boot.actuator.health.ReadinessCheckCallbackProcessor; import com.alipay.sofa.boot.actuator.health.ReadinessCheckListener; import com.alipay.sofa.boot.actuator.health.ReadinessEndpoint; +import com.alipay.sofa.boot.autoconfigure.condition.OnVirtualThreadStartupAvailableCondition; +import com.alipay.sofa.boot.autoconfigure.condition.OnVirtualThreadStartupDisableCondition; import com.alipay.sofa.boot.constant.SofaBootConstants; import com.alipay.sofa.boot.log.SofaBootLoggerFactory; import com.alipay.sofa.common.thread.NamedThreadFactory; import com.alipay.sofa.common.thread.SofaThreadPoolExecutor; +import com.alipay.sofa.common.thread.virtual.SofaVirtualThreadFactory; import org.slf4j.Logger; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -32,7 +35,9 @@ 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.Conditional; +import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -57,7 +62,7 @@ public class ReadinessAutoConfiguration { public ReadinessCheckListener readinessCheckListener(HealthCheckerProcessor healthCheckerProcessor, HealthIndicatorProcessor healthIndicatorProcessor, ReadinessCheckCallbackProcessor afterReadinessCheckCallbackProcessor, - ThreadPoolExecutor readinessHealthCheckExecutor, + ExecutorService readinessHealthCheckExecutor, HealthProperties healthCheckProperties) { ReadinessCheckListener readinessCheckListener = new ReadinessCheckListener( healthCheckerProcessor, healthIndicatorProcessor, afterReadinessCheckCallbackProcessor); @@ -76,7 +81,7 @@ public ReadinessCheckListener readinessCheckListener(HealthCheckerProcessor heal @Bean @ConditionalOnMissingBean public HealthCheckerProcessor healthCheckerProcessor(HealthProperties healthCheckProperties, - ThreadPoolExecutor readinessHealthCheckExecutor) { + ExecutorService readinessHealthCheckExecutor) { HealthCheckerProcessor healthCheckerProcessor = new HealthCheckerProcessor(); healthCheckerProcessor.setHealthCheckExecutor(readinessHealthCheckExecutor); healthCheckerProcessor.setParallelCheck(healthCheckProperties.isParallelCheck()); @@ -92,7 +97,7 @@ public HealthCheckerProcessor healthCheckerProcessor(HealthProperties healthChec @Bean @ConditionalOnMissingBean public HealthIndicatorProcessor healthIndicatorProcessor(HealthProperties healthCheckProperties, - ThreadPoolExecutor readinessHealthCheckExecutor) { + ExecutorService readinessHealthCheckExecutor) { HealthIndicatorProcessor healthIndicatorProcessor = new HealthIndicatorProcessor(); healthIndicatorProcessor.setHealthCheckExecutor(readinessHealthCheckExecutor); healthIndicatorProcessor.initExcludedIndicators(healthCheckProperties @@ -115,7 +120,8 @@ public ReadinessCheckCallbackProcessor afterReadinessCheckCallbackProcessor() { @Bean(name = ReadinessCheckListener.READINESS_HEALTH_CHECK_EXECUTOR_BEAN_NAME) @ConditionalOnMissingBean(name = ReadinessCheckListener.READINESS_HEALTH_CHECK_EXECUTOR_BEAN_NAME) - public ThreadPoolExecutor readinessHealthCheckExecutor(HealthProperties properties) { + @Conditional(OnVirtualThreadStartupDisableCondition.class) + public ExecutorService readinessHealthCheckExecutor(HealthProperties properties) { int threadPoolSize; if (properties.isParallelCheck()) { threadPoolSize = SofaBootConstants.CPU_CORE * 5; @@ -129,4 +135,12 @@ public ThreadPoolExecutor readinessHealthCheckExecutor(HealthProperties properti new ThreadPoolExecutor.CallerRunsPolicy(), "health-check", SofaBootConstants.SOFA_BOOT_SPACE_NAME); } + + @Bean(name = ReadinessCheckListener.READINESS_HEALTH_CHECK_EXECUTOR_BEAN_NAME) + @ConditionalOnMissingBean(name = ReadinessCheckListener.READINESS_HEALTH_CHECK_EXECUTOR_BEAN_NAME) + @Conditional(OnVirtualThreadStartupAvailableCondition.class) + public ExecutorService readinessHealthCheckVirtualExecutor() { + LOGGER.info("Create health-check virtual executor service"); + return SofaVirtualThreadFactory.ofExecutorService("health-check"); + } } diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfiguration.java b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfiguration.java index f26909185..792c160d5 100644 --- a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfiguration.java @@ -16,7 +16,10 @@ */ package com.alipay.sofa.boot.actuator.autoconfigure.rpc; +import com.alipay.sofa.boot.actuator.autoconfigure.health.ReadinessAutoConfiguration; +import com.alipay.sofa.boot.actuator.health.ReadinessCheckListener; import com.alipay.sofa.boot.actuator.health.ReadinessEndpoint; +import com.alipay.sofa.boot.actuator.rpc.HealthCheckProviderConfigDelayRegisterChecker; import com.alipay.sofa.boot.actuator.rpc.RpcAfterHealthCheckCallback; import com.alipay.sofa.boot.actuator.rpc.SofaRpcEndpoint; import com.alipay.sofa.boot.autoconfigure.rpc.SofaRpcAutoConfiguration; @@ -35,7 +38,7 @@ * @author huzijie * @version RpcActuatorAutoConfiguration.java, v 0.1 2023年02月08日 4:45 PM huzijie Exp $ */ -@AutoConfiguration(after = SofaRpcAutoConfiguration.class) +@AutoConfiguration(after = { SofaRpcAutoConfiguration.class, ReadinessAutoConfiguration.class }) @ConditionalOnClass(RpcStartApplicationListener.class) @ConditionalOnBean(RpcStartApplicationListener.class) public class RpcActuatorAutoConfiguration { @@ -53,4 +56,12 @@ public RpcAfterHealthCheckCallback rpcAfterHealthCheckCallback(RpcStartApplicati public SofaRpcEndpoint sofaRpcEndPoint() { return new SofaRpcEndpoint(); } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint(endpoint = ReadinessEndpoint.class) + @ConditionalOnBean(ReadinessCheckListener.class) + public HealthCheckProviderConfigDelayRegisterChecker healthCheckProviderConfigDelayRegisterChecker(ReadinessCheckListener readinessCheckListener) { + return new HealthCheckProviderConfigDelayRegisterChecker(readinessCheckListener); + } } diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/threadpool/ThreadPoolEndpointAutoConfiguration.java b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/threadpool/ThreadPoolEndpointAutoConfiguration.java new file mode 100644 index 000000000..fe7291be8 --- /dev/null +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/threadpool/ThreadPoolEndpointAutoConfiguration.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.actuator.autoconfigure.threadpool; + +import com.alipay.sofa.boot.actuator.threadpool.ThreadPoolEndpoint; +import com.alipay.sofa.common.thread.ThreadPoolGovernor; +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for {@link ThreadPoolEndpoint}. + * + * @author huzijie + * @version ThreadPoolEndpointAutoConfiguration.java, v 0.1 2024年03月22日 11:58 huzijie Exp $ + */ +@AutoConfiguration +@ConditionalOnAvailableEndpoint(endpoint = ThreadPoolEndpoint.class) +public class ThreadPoolEndpointAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public ThreadPoolEndpoint threadPoolEndpoint() { + return new ThreadPoolEndpoint(ThreadPoolGovernor.getInstance()); + } +} diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 7ad555319..6dbe5466a 100644 --- a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -7,4 +7,5 @@ com.alipay.sofa.boot.actuator.autoconfigure.health.ReadinessRuntimeAutoConfigura com.alipay.sofa.boot.actuator.autoconfigure.health.ManualReadinessCallbackEndpointAutoConfiguration com.alipay.sofa.boot.actuator.autoconfigure.startup.StartupEndPointAutoConfiguration com.alipay.sofa.boot.actuator.autoconfigure.rpc.RpcActuatorAutoConfiguration -com.alipay.sofa.boot.actuator.autoconfigure.isle.IsleEndpointAutoConfiguration \ No newline at end of file +com.alipay.sofa.boot.actuator.autoconfigure.isle.IsleEndpointAutoConfiguration +com.alipay.sofa.boot.actuator.autoconfigure.threadpool.ThreadPoolEndpointAutoConfiguration \ No newline at end of file diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfigurationTests.java b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfigurationTests.java index 6ac6b8b5e..caf3ada77 100644 --- a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfigurationTests.java +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/health/ReadinessAutoConfigurationTests.java @@ -27,10 +27,13 @@ import com.alipay.sofa.boot.isle.ApplicationRuntimeModel; import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import static org.assertj.core.api.Assertions.assertThat; @@ -177,4 +180,26 @@ void runWhenNotHaveRuntimeConfigurationWithoutBeans() { .withClassLoader(new FilteredClassLoader(SofaRuntimeContext.class)) .run((context) -> assertThat(context).doesNotHaveBean(ComponentHealthChecker.class)); } + + @Test + public void useReadinessHealthCheckExecutor() { + this.contextRunner + .run((context) -> { + ExecutorService threadPoolExecutor = context.getBean(ReadinessCheckListener.READINESS_HEALTH_CHECK_EXECUTOR_BEAN_NAME, + ExecutorService.class); + assertThat(threadPoolExecutor).isInstanceOf(ThreadPoolExecutor.class); + }); + } + + @Test + @EnabledOnJre(JRE.JAVA_21) + public void useReadinessHealthCheckVirtualExecutor() { + this.contextRunner + .withPropertyValues("sofa.boot.startup.threads.virtual.enabled=true") + .run((context) -> { + ExecutorService threadPoolExecutor = context.getBean(ReadinessCheckListener.READINESS_HEALTH_CHECK_EXECUTOR_BEAN_NAME, + ExecutorService.class); + assertThat(threadPoolExecutor).isNotInstanceOf(ThreadPoolExecutor.class); + }); + } } diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfigurationTests.java b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfigurationTests.java index bd0e3073a..a72f858c0 100644 --- a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfigurationTests.java +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/rpc/RpcActuatorAutoConfigurationTests.java @@ -16,6 +16,8 @@ */ package com.alipay.sofa.boot.actuator.autoconfigure.rpc; +import com.alipay.sofa.boot.actuator.autoconfigure.health.ReadinessAutoConfiguration; +import com.alipay.sofa.boot.actuator.rpc.HealthCheckProviderConfigDelayRegisterChecker; import com.alipay.sofa.boot.actuator.rpc.RpcAfterHealthCheckCallback; import com.alipay.sofa.boot.actuator.rpc.SofaRpcEndpoint; import com.alipay.sofa.rpc.boot.context.RpcStartApplicationListener; @@ -41,6 +43,15 @@ public class RpcActuatorAutoConfigurationTests { .withPropertyValues( "management.endpoints.web.exposure.include=readiness,rpc"); + @Test + void runShouldHaveHealthCheckProviderConfigDelayRegisterChecker() { + this.contextRunner + .withUserConfiguration(ReadinessAutoConfiguration.class) + .withBean(RpcStartApplicationListener.class) + .run((context) -> assertThat(context) + .hasSingleBean(HealthCheckProviderConfigDelayRegisterChecker.class)); + } + @Test void runShouldHaveRpcActuatorBeans() { this.contextRunner diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/threadpool/ThreadPoolEndpointAutoConfigurationTests.java b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/threadpool/ThreadPoolEndpointAutoConfigurationTests.java new file mode 100644 index 000000000..8585ae9cf --- /dev/null +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/test/java/com/alipay/sofa/boot/actuator/autoconfigure/threadpool/ThreadPoolEndpointAutoConfigurationTests.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 com.alipay.sofa.boot.actuator.autoconfigure.threadpool; + +import com.alipay.sofa.boot.actuator.threadpool.ThreadPoolEndpoint; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ThreadPoolEndpointAutoConfiguration}. + * + * @author huzijie + * @version ThreadPoolEndpointAutoConfigurationTests.java, v 0.1 2024年03月22日 11:59 huzijie Exp $ + */ +public class ThreadPoolEndpointAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations + .of(ThreadPoolEndpointAutoConfiguration.class)); + + @Test + void runShouldHaveEndpointBean() { + this.contextRunner.withPropertyValues("management.endpoints.web.exposure.include=threadpool") + .run((context) -> assertThat(context).hasSingleBean(ThreadPoolEndpoint.class)); + } + + @Test + void runWhenNotExposedShouldNotHaveEndpointBean() { + this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ThreadPoolEndpoint.class)); + } +} diff --git a/sofa-boot-project/sofa-boot-actuator/pom.xml b/sofa-boot-project/sofa-boot-actuator/pom.xml index 8f384d89a..8b2f7c7c6 100644 --- a/sofa-boot-project/sofa-boot-actuator/pom.xml +++ b/sofa-boot-project/sofa-boot-actuator/pom.xml @@ -10,6 +10,7 @@ 4.0.0 + SOFABoot Actuator sofa-boot-actuator diff --git a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/beans/IsleBeansEndpoint.java b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/beans/IsleBeansEndpoint.java index db297c5eb..ec1710977 100644 --- a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/beans/IsleBeansEndpoint.java +++ b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/beans/IsleBeansEndpoint.java @@ -60,19 +60,33 @@ public IsleBeansEndpoint(ConfigurableApplicationContext context, @Override public BeansEndpoint.BeansDescriptor beans() { BeansEndpoint.BeansDescriptor beansDescriptor = super.beans(); - Map moduleApplicationContexts = getModuleApplicationContexts(applicationRuntimeModel); + String springParentId = null; + for (Map.Entry entry : beansDescriptor.getContexts() + .entrySet()) { + if (entry.getValue().getParentId() == null) { + springParentId = entry.getKey(); + break; + } + } + + Map moduleApplicationContexts = getModuleApplicationContexts( + applicationRuntimeModel, springParentId); beansDescriptor.getContexts().putAll(moduleApplicationContexts); return beansDescriptor; } - private Map getModuleApplicationContexts(ApplicationRuntimeModel applicationRuntimeModel) { + private Map getModuleApplicationContexts(ApplicationRuntimeModel applicationRuntimeModel,String springParentId) { Map contexts = new HashMap<>(); List installedModules = applicationRuntimeModel.getInstalled(); installedModules.forEach(descriptor -> { ApplicationContext applicationContext = descriptor.getApplicationContext(); + String parentId = descriptor.getSpringParent(); + if (parentId == null){ + parentId = springParentId; + } if (applicationContext instanceof ConfigurableApplicationContext) { BeansEndpoint.ContextBeansDescriptor contextBeans = describing((ConfigurableApplicationContext) applicationContext, - descriptor.getSpringParent()); + parentId); if (contextBeans != null) { contexts.put(descriptor.getModuleName(), contextBeans); } diff --git a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthCheckerProcessor.java b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthCheckerProcessor.java index c84ae5ecc..55c2a4cef 100644 --- a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthCheckerProcessor.java +++ b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthCheckerProcessor.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -106,24 +107,13 @@ public boolean readinessHealthCheck(Map healthMap) { String checkComponentNames = readinessHealthCheckers.values().stream() .map(HealthChecker::getComponentName).collect(Collectors.joining(",")); logger.info("SOFABoot HealthChecker readiness check {} item: {}.", - healthCheckers.size(), checkComponentNames); + readinessHealthCheckers.size(), checkComponentNames); boolean result; if (isParallelCheck()) { - CountDownLatch countDownLatch = new CountDownLatch(healthCheckers.size()); + CountDownLatch countDownLatch = new CountDownLatch(readinessHealthCheckers.size()); AtomicBoolean parallelResult = new AtomicBoolean(true); - healthCheckers.forEach((String key, HealthChecker value) -> healthCheckExecutor.execute(() -> { - try { - if (!doHealthCheck(key, value, false, healthMap, true, false)) { - parallelResult.set(false); - } - } catch (Throwable t) { - parallelResult.set(false); - logger.error(ErrorCode.convert("01-22004"), t); - healthMap.put(key, new Health.Builder().withException(t).status(Status.DOWN).build()); - } finally { - countDownLatch.countDown(); - } - })); + readinessHealthCheckers.forEach((String key, HealthChecker value) -> healthCheckExecutor.execute( + new AsyncHealthCheckRunnable(key, value, healthMap, parallelResult, countDownLatch))); boolean finished = false; try { finished = countDownLatch.await(getParallelCheckTimeout(), TimeUnit.MILLISECONDS); @@ -160,7 +150,7 @@ public boolean readinessHealthCheck(Map healthMap) { * @return health check passes or not */ private boolean doHealthCheck(String beanId, HealthChecker healthChecker, boolean isRetry, - Map healthMap, boolean isReadiness, boolean wait) { + Map healthMap, boolean isReadiness, boolean wait) { Assert.notNull(healthMap, "HealthMap must not be null"); Health health; @@ -180,20 +170,23 @@ private boolean doHealthCheck(String beanId, HealthChecker healthChecker, boolea do { try { if (wait) { - Future future = healthCheckExecutor.submit(healthChecker::isHealthy); + Future future = healthCheckExecutor + .submit(new AsyncHealthCheckCallable(healthChecker)); health = future.get(timeout, TimeUnit.MILLISECONDS); } else { health = healthChecker.isHealthy(); } - } catch (TimeoutException e) { - logger.error( + } catch (TimeoutException e) { + logger + .error( "Timeout occurred while doing HealthChecker[{}] {} check, the timeout value is: {}ms.", beanId, checkType, timeout); - health = new Health.Builder().withException(e).withDetail("timeout", timeout).status(Status.UNKNOWN).build(); + health = new Health.Builder().withException(e).withDetail("timeout", timeout) + .status(Status.UNKNOWN).build(); } catch (Throwable e) { logger.error(String.format( - "Exception occurred while wait the result of HealthChecker[%s] %s check.", - beanId, checkType), e); + "Exception occurred while wait the result of HealthChecker[%s] %s check.", + beanId, checkType), e); health = new Health.Builder().withException(e).status(Status.DOWN).build(); } result = health.getStatus().equals(Status.UP); @@ -208,9 +201,7 @@ private boolean doHealthCheck(String beanId, HealthChecker healthChecker, boolea retryCount += 1; TimeUnit.MILLISECONDS.sleep(healthChecker.getRetryTimeInterval()); } catch (InterruptedException e) { - logger - .error(ErrorCode.convert("01-23002", retryCount, beanId, - checkType), e); + logger.error(ErrorCode.convert("01-23002", retryCount, beanId, checkType), e); } } } while (isRetry && retryCount < healthChecker.getRetryCount()); @@ -223,12 +214,12 @@ private boolean doHealthCheck(String beanId, HealthChecker healthChecker, boolea if (!result) { if (healthChecker.isStrictCheck()) { logger.error(ErrorCode.convert("01-23001", beanId, checkType, retryCount, - objectMapper.writeValueAsString(health.getDetails()), - healthChecker.isStrictCheck())); + objectMapper.writeValueAsString(health.getDetails()), + healthChecker.isStrictCheck())); } else { logger.warn(ErrorCode.convert("01-23001", beanId, checkType, retryCount, - objectMapper.writeValueAsString(health.getDetails()), - healthChecker.isStrictCheck())); + objectMapper.writeValueAsString(health.getDetails()), + healthChecker.isStrictCheck())); } } } catch (JsonProcessingException ex) { @@ -364,4 +355,55 @@ public int getTimeout() { } } + + private class AsyncHealthCheckRunnable implements Runnable { + private final String key; + private final HealthChecker value; + private final Map healthMap; + + private final AtomicBoolean parallelResult; + + private final CountDownLatch countDownLatch; + + public AsyncHealthCheckRunnable(String key, HealthChecker value, + Map healthMap, + AtomicBoolean parallelResult, CountDownLatch countDownLatch) { + this.key = key; + this.value = value; + this.healthMap = healthMap; + this.parallelResult = parallelResult; + this.countDownLatch = countDownLatch; + } + + @Override + public void run() { + try { + if (!HealthCheckerProcessor.this.doHealthCheck(key, value, false, healthMap, true, + false)) { + parallelResult.set(false); + } + } catch (Throwable t) { + parallelResult.set(false); + logger.error(ErrorCode.convert("01-22004"), t); + healthMap.put(key, new Health.Builder().withException(t).status(Status.DOWN) + .build()); + } finally { + countDownLatch.countDown(); + } + } + } + + private class AsyncHealthCheckCallable implements Callable { + + private final HealthChecker healthChecker; + + public AsyncHealthCheckCallable(HealthChecker healthChecker) { + this.healthChecker = healthChecker; + } + + @Override + public Health call() throws Exception { + return healthChecker.isHealthy(); + } + } } diff --git a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthIndicatorProcessor.java b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthIndicatorProcessor.java index c5caae897..74dbb833b 100644 --- a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthIndicatorProcessor.java +++ b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/HealthIndicatorProcessor.java @@ -41,6 +41,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -67,7 +68,8 @@ public class HealthIndicatorProcessor implements ApplicationContextAware { .asList( "com.alipay.sofa.boot.actuator.health.NonReadinessCheck", "org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator", - "org.springframework.boot.actuate.availability.LivenessStateHealthIndicator"); + "org.springframework.boot.actuate.availability.LivenessStateHealthIndicator", + "org.springframework.boot.actuate.ssl.SslHealthIndicator"); private static final String REACTOR_CLASS = "reactor.core.publisher.Mono"; @@ -167,19 +169,8 @@ public boolean readinessHealthCheck(Map healthMap) { if (isParallelCheck()) { CountDownLatch countDownLatch = new CountDownLatch(healthIndicators.size()); AtomicBoolean parallelResult = new AtomicBoolean(true); - healthIndicators.forEach((key, value) -> healthCheckExecutor.execute(() -> { - try { - if (!doHealthCheck(key, value, healthMap, false)) { - parallelResult.set(false); - } - } catch (Throwable t) { - parallelResult.set(false); - logger.error(ErrorCode.convert("01-21003"), t); - healthMap.put(key, new Health.Builder().withException(t).status(Status.DOWN).build()); - } finally { - countDownLatch.countDown(); - } - })); + healthIndicators.forEach((key, value) -> healthCheckExecutor.execute( + new AsyncHealthIndicatorRunnable(key, value, healthMap, parallelResult, countDownLatch))); boolean finished = false; try { finished = countDownLatch.await(getParallelCheckTimeout(), TimeUnit.MILLISECONDS); @@ -226,7 +217,7 @@ public boolean doHealthCheck(String beanId, HealthIndicator healthIndicator, try { if (wait) { Future future = healthCheckExecutor - .submit(healthIndicator::health); + .submit(new AsyncHealthIndicatorCallable(healthIndicator)); health = future.get(timeout, TimeUnit.MILLISECONDS); } else { health = healthIndicator.health(); @@ -315,4 +306,55 @@ public void setHealthIndicatorConfig(Map healthIndi public List getHealthIndicatorStartupStatList() { return healthIndicatorStartupStatList; } + + private class AsyncHealthIndicatorRunnable implements Runnable { + private final String key; + private final HealthIndicator value; + private final Map healthMap; + + private final AtomicBoolean parallelResult; + + private final CountDownLatch countDownLatch; + + public AsyncHealthIndicatorRunnable(String key, HealthIndicator value, + Map healthMap, + AtomicBoolean parallelResult, + CountDownLatch countDownLatch) { + this.key = key; + this.value = value; + this.healthMap = healthMap; + this.parallelResult = parallelResult; + this.countDownLatch = countDownLatch; + } + + @Override + public void run() { + try { + if (!HealthIndicatorProcessor.this.doHealthCheck(key, value, healthMap, false)) { + parallelResult.set(false); + } + } catch (Throwable t) { + parallelResult.set(false); + logger.error(ErrorCode.convert("01-21003"), t); + healthMap.put(key, new Health.Builder().withException(t).status(Status.DOWN) + .build()); + } finally { + countDownLatch.countDown(); + } + } + } + + private class AsyncHealthIndicatorCallable implements Callable { + + private final HealthIndicator healthIndicator; + + public AsyncHealthIndicatorCallable(HealthIndicator healthIndicator) { + this.healthIndicator = healthIndicator; + } + + @Override + public Health call() throws Exception { + return healthIndicator.health(); + } + } } diff --git a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckCallbackProcessor.java b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckCallbackProcessor.java index 7c412f877..26fe97aaf 100644 --- a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckCallbackProcessor.java +++ b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckCallbackProcessor.java @@ -112,7 +112,7 @@ private boolean doHealthCheckCallback(String beanId, BaseStat baseStat = new BaseStat(); baseStat.setName(beanId); - baseStat.putAttribute("type", "readinessCheckCallbacn"); + baseStat.putAttribute("type", "readinessCheckCallback"); baseStat.setStartTime(System.currentTimeMillis()); try { diff --git a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckListener.java b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckListener.java index 07fc48957..d0b3e8f87 100644 --- a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckListener.java +++ b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/health/ReadinessCheckListener.java @@ -42,7 +42,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -96,7 +97,7 @@ public class ReadinessCheckListener implements ApplicationContextAware, Ordered, /** * Check result details for healthCheckerProcessor */ - private final Map healthCheckerDetails = new HashMap<>(); + private final Map healthCheckerDetails = new ConcurrentHashMap<>(); /** * Check result for healthIndicatorProcessor @@ -106,7 +107,7 @@ public class ReadinessCheckListener implements ApplicationContextAware, Ordered, /** * Check result for healthIndicatorProcessor */ - private final Map healthIndicatorDetails = new HashMap<>(); + private final Map healthIndicatorDetails = new ConcurrentHashMap<>(); /** * Check result for readinessCheckCallbackProcessor @@ -116,7 +117,7 @@ public class ReadinessCheckListener implements ApplicationContextAware, Ordered, /** * Check result details for readinessCheckCallbackProcessor */ - private final Map healthCallbackDetails = new HashMap<>(); + private final Map healthCallbackDetails = new ConcurrentHashMap<>(); /** * ReadinessCheckCallbackProcessor trigger status @@ -148,7 +149,7 @@ public class ReadinessCheckListener implements ApplicationContextAware, Ordered, private boolean throwExceptionWhenHealthCheckFailed = false; - private ThreadPoolExecutor healthCheckExecutor; + private ExecutorService executorService; public ReadinessCheckListener(HealthCheckerProcessor healthCheckerProcessor, HealthIndicatorProcessor healthIndicatorProcessor, @@ -232,7 +233,7 @@ public void onContextRefreshedEvent(ContextRefreshedEvent event) { if (startupReporter != null) { startupReporter.addCommonStartupStat(stat); } - healthCheckExecutor.shutdown(); + executorService.shutdown(); } } @@ -454,8 +455,8 @@ public ReadinessState getReadinessState() { return readinessState; } - public void setHealthCheckExecutor(ThreadPoolExecutor healthCheckExecutor) { - this.healthCheckExecutor = healthCheckExecutor; + public void setHealthCheckExecutor(ExecutorService executorService) { + this.executorService = executorService; } public static class ManualReadinessCallbackResult { diff --git a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/rpc/HealthCheckProviderConfigDelayRegisterChecker.java b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/rpc/HealthCheckProviderConfigDelayRegisterChecker.java new file mode 100644 index 000000000..7526ca42d --- /dev/null +++ b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/rpc/HealthCheckProviderConfigDelayRegisterChecker.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 com.alipay.sofa.boot.actuator.rpc; + +import com.alipay.sofa.boot.actuator.health.ReadinessCheckListener; +import com.alipay.sofa.rpc.boot.container.ProviderConfigDelayRegisterChecker; +import org.springframework.boot.availability.ReadinessState; + +/** + * ProviderConfigDelayRegisterChecker的具体实现 + */ +public class HealthCheckProviderConfigDelayRegisterChecker implements + ProviderConfigDelayRegisterChecker { + + private final ReadinessCheckListener readinessCheckListener; + + public HealthCheckProviderConfigDelayRegisterChecker(ReadinessCheckListener readinessCheckListener) { + this.readinessCheckListener = readinessCheckListener; + } + + @Override + public boolean allowRegister() { + return ReadinessState.REFUSING_TRAFFIC != readinessCheckListener.getReadinessState(); + } +} diff --git a/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/threadpool/ThreadPoolEndpoint.java b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/threadpool/ThreadPoolEndpoint.java new file mode 100644 index 000000000..ef73648c5 --- /dev/null +++ b/sofa-boot-project/sofa-boot-actuator/src/main/java/com/alipay/sofa/boot/actuator/threadpool/ThreadPoolEndpoint.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 com.alipay.sofa.boot.actuator.threadpool; + +import com.alipay.sofa.common.thread.ThreadPoolConfig; +import com.alipay.sofa.common.thread.ThreadPoolGovernor; +import com.alipay.sofa.common.thread.ThreadPoolMonitorWrapper; +import org.springframework.boot.actuate.endpoint.OperationResponseBody; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * {@link Endpoint @Endpoint} to expose details of sofa thread pools registered in {@link ThreadPoolGovernor}. + * + * @author huzijie + * @version ThreadPoolEndpoint.java, v 0.1 2024年03月22日 11:41 huzijie Exp $ + */ +@Endpoint(id = "threadpool") +public class ThreadPoolEndpoint { + + private final ThreadPoolGovernor threadPoolGovernor; + + public ThreadPoolEndpoint(ThreadPoolGovernor threadPoolGovernor) { + this.threadPoolGovernor = threadPoolGovernor; + } + + @ReadOperation + public ThreadPoolsDescriptor threadPools () { + Collection threadPoolWrappers = threadPoolGovernor.getAllThreadPoolWrappers(); + + List threadPoolInfoList = threadPoolWrappers.stream().map(this::convertToThreadPoolInfo).toList(); + return new ThreadPoolsDescriptor(threadPoolInfoList); + } + + private ThreadPoolInfo convertToThreadPoolInfo(ThreadPoolMonitorWrapper wrapper) { + ThreadPoolConfig threadPoolConfig = wrapper.getThreadPoolConfig(); + String threadPoolName = threadPoolConfig.getThreadPoolName(); + String spaceName = threadPoolConfig.getSpaceName(); + long period = threadPoolConfig.getPeriod(); + long taskTimeout = threadPoolConfig.getTaskTimeoutMilli(); + + ThreadPoolExecutor threadPoolExecutor = wrapper.getThreadPoolExecutor(); + String threadPoolClassName = threadPoolExecutor.getClass().getName(); + int coreSize = threadPoolExecutor.getCorePoolSize(); + int maxSize = threadPoolExecutor.getMaximumPoolSize(); + int queueSize = threadPoolExecutor.getQueue().size(); + int queueRemainingCapacity = threadPoolExecutor.getQueue().remainingCapacity(); + String queueClassName = threadPoolExecutor.getQueue().getClass().getName(); + + return new ThreadPoolInfo(threadPoolName, spaceName, threadPoolClassName, coreSize, maxSize, queueClassName, queueSize, queueRemainingCapacity, period, taskTimeout); + } + + public record ThreadPoolsDescriptor(List threadPoolInfoList) implements OperationResponseBody {} + + public record ThreadPoolInfo(String threadPoolName, + String spaceName, + + String threadPoolClassName, + int coreSize, + int maxSize, + String queueClassName, + int queueSize, + int queueRemainingCapacity, + long monitorPeriod, + long taskTimeout) {} +} diff --git a/sofa-boot-project/sofa-boot-actuator/src/test/java/com/alipay/sofa/boot/actuator/rpc/HealthCheckProviderConfigDelayRegisterCheckerTest.java b/sofa-boot-project/sofa-boot-actuator/src/test/java/com/alipay/sofa/boot/actuator/rpc/HealthCheckProviderConfigDelayRegisterCheckerTest.java new file mode 100644 index 000000000..7aa708bd1 --- /dev/null +++ b/sofa-boot-project/sofa-boot-actuator/src/test/java/com/alipay/sofa/boot/actuator/rpc/HealthCheckProviderConfigDelayRegisterCheckerTest.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.actuator.rpc; + +import com.alipay.sofa.boot.actuator.health.ReadinessCheckListener; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.availability.ReadinessState; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link HealthCheckProviderConfigDelayRegisterChecker} + */ +@ExtendWith(MockitoExtension.class) +public class HealthCheckProviderConfigDelayRegisterCheckerTest { + + @InjectMocks + private HealthCheckProviderConfigDelayRegisterChecker healthCheckProviderConfigDelayRegisterChecker; + + @Mock + private ReadinessCheckListener readinessCheckListener; + + @Test + public void testAllowRegister() { + // Setup + Mockito.doReturn(ReadinessState.ACCEPTING_TRAFFIC).when(readinessCheckListener) + .getReadinessState(); + // Run the test + boolean result = healthCheckProviderConfigDelayRegisterChecker.allowRegister(); + + // Verify the results + assertThat(result).isTrue(); + } + +} diff --git a/sofa-boot-project/sofa-boot-actuator/src/test/java/com/alipay/sofa/boot/actuator/threadpool/ThreadPoolEndpointTests.java b/sofa-boot-project/sofa-boot-actuator/src/test/java/com/alipay/sofa/boot/actuator/threadpool/ThreadPoolEndpointTests.java new file mode 100644 index 000000000..6860be43e --- /dev/null +++ b/sofa-boot-project/sofa-boot-actuator/src/test/java/com/alipay/sofa/boot/actuator/threadpool/ThreadPoolEndpointTests.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 com.alipay.sofa.boot.actuator.threadpool; + +import com.alipay.sofa.common.thread.ThreadPoolConfig; +import com.alipay.sofa.common.thread.ThreadPoolGovernor; +import com.alipay.sofa.common.thread.ThreadPoolMonitorWrapper; +import com.alipay.sofa.common.thread.ThreadPoolStatistics; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +/** + * @author huzijie + * @version ThreadPoolEndpointTests.java, v 0.1 2024年03月22日 12:01 huzijie Exp $ + */ +@ExtendWith(MockitoExtension.class) +public class ThreadPoolEndpointTests { + + @Mock + private ThreadPoolGovernor threadPoolGovernor; + + @InjectMocks + private ThreadPoolEndpoint threadPoolEndpoint; + + @BeforeEach + public void setUp() { + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 6, 10, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(8)); + + ThreadPoolConfig threadPoolConfig = Mockito.mock(ThreadPoolConfig.class); + when(threadPoolConfig.getThreadPoolName()).thenReturn("mockThreadPoolName"); + when(threadPoolConfig.getSpaceName()).thenReturn("mockSpaceName"); + when(threadPoolConfig.getTaskTimeoutMilli()).thenReturn(10L); + when(threadPoolConfig.getPeriod()).thenReturn(9L); + + ThreadPoolStatistics threadPoolStatistics = new ThreadPoolStatistics(threadPoolExecutor); + ThreadPoolMonitorWrapper threadPoolMonitorWrapper = new ThreadPoolMonitorWrapper( + threadPoolExecutor, threadPoolConfig, threadPoolStatistics); + when(threadPoolGovernor.getAllThreadPoolWrappers()).thenReturn( + List.of(threadPoolMonitorWrapper)); + } + + @Test + public void threadPools() { + List descriptor = threadPoolEndpoint.threadPools() + .threadPoolInfoList(); + assertThat(descriptor).hasSize(1); + ThreadPoolEndpoint.ThreadPoolInfo threadPoolInfo = descriptor.get(0); + assertThat(threadPoolInfo.threadPoolName()).isEqualTo("mockThreadPoolName"); + assertThat(threadPoolInfo.spaceName()).isEqualTo("mockSpaceName"); + assertThat(threadPoolInfo.threadPoolClassName()).isEqualTo( + "java.util.concurrent.ThreadPoolExecutor"); + assertThat(threadPoolInfo.coreSize()).isEqualTo(5); + assertThat(threadPoolInfo.maxSize()).isEqualTo(6); + assertThat(threadPoolInfo.queueSize()).isEqualTo(0); + assertThat(threadPoolInfo.queueRemainingCapacity()).isEqualTo(8); + assertThat(threadPoolInfo.queueClassName()).isEqualTo( + "java.util.concurrent.LinkedBlockingQueue"); + assertThat(threadPoolInfo.monitorPeriod()).isEqualTo(9); + assertThat(threadPoolInfo.taskTimeout()).isEqualTo(10); + } +} diff --git a/sofa-boot-project/sofa-boot-autoconfigure/pom.xml b/sofa-boot-project/sofa-boot-autoconfigure/pom.xml index d5f3012f5..49894462a 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/pom.xml +++ b/sofa-boot-project/sofa-boot-autoconfigure/pom.xml @@ -11,6 +11,7 @@ 4.0.0 + SOFABoot Autoconfigure sofa-boot-autoconfigure diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupAvailableCondition.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupAvailableCondition.java new file mode 100644 index 000000000..dfde779cb --- /dev/null +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupAvailableCondition.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 com.alipay.sofa.boot.autoconfigure.condition; + +import org.springframework.boot.autoconfigure.condition.AllNestedConditions; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.system.JavaVersion; + +/** + * Condition for startup SOFABoot on virtual thread. + * + * @author huzijie + * @version OnVirtualThreadStartupAvailableCondition.java, v 0.1 2023年12月05日 4:51 PM huzijie Exp $ + */ +public class OnVirtualThreadStartupAvailableCondition extends AllNestedConditions { + public OnVirtualThreadStartupAvailableCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnJava(value = JavaVersion.TWENTY_ONE) + static class JdkVersionAvailable { + + } + + @ConditionalOnProperty(value = "sofa.boot.startup.threads.virtual.enabled", havingValue = "true") + static class IslePropertyAvailable { + + } +} diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupDisableCondition.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupDisableCondition.java new file mode 100644 index 000000000..49d82ccd0 --- /dev/null +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupDisableCondition.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 com.alipay.sofa.boot.autoconfigure.condition; + +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.system.JavaVersion; + +/** + * Condition for not startup SOFABoot on virtual thread. + * + * @author huzijie + * @version StartupOnVirtualThreadDisableCondition.java, v 0.1 2023年12月05日 4:51 PM huzijie Exp $ + */ +public class OnVirtualThreadStartupDisableCondition extends AnyNestedCondition { + public OnVirtualThreadStartupDisableCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnJava(value = JavaVersion.TWENTY_ONE, range = ConditionalOnJava.Range.OLDER_THAN) + static class JdkVersionAvailable { + + } + + @ConditionalOnProperty(value = "sofa.boot.startup.threads.virtual.enabled", havingValue = "false", matchIfMissing = true) + static class IslePropertyAvailable { + + } +} diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfiguration.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfiguration.java index 2117eefd2..576a19d40 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfiguration.java @@ -16,6 +16,8 @@ */ package com.alipay.sofa.boot.autoconfigure.isle; +import com.alipay.sofa.boot.autoconfigure.condition.OnVirtualThreadStartupAvailableCondition; +import com.alipay.sofa.boot.autoconfigure.condition.OnVirtualThreadStartupDisableCondition; import com.alipay.sofa.boot.constant.SofaBootConstants; import com.alipay.sofa.boot.context.ContextRefreshInterceptor; import com.alipay.sofa.boot.context.processor.SofaPostProcessorShareFilter; @@ -37,6 +39,7 @@ import com.alipay.sofa.boot.log.SofaBootLoggerFactory; import com.alipay.sofa.common.thread.NamedThreadFactory; import com.alipay.sofa.common.thread.SofaThreadPoolExecutor; +import com.alipay.sofa.common.thread.virtual.SofaVirtualThreadFactory; import org.slf4j.Logger; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -51,6 +54,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -152,7 +156,8 @@ public SpringContextLoader sofaDynamicSpringContextLoader(SofaModuleProperties s @Bean(SpringContextInstallStage.SOFA_MODULE_REFRESH_EXECUTOR_BEAN_NAME) @ConditionalOnMissingBean(name = SpringContextInstallStage.SOFA_MODULE_REFRESH_EXECUTOR_BEAN_NAME) @ConditionalOnProperty(value = "sofa.boot.isle.moduleStartUpParallel", havingValue = "true", matchIfMissing = true) - public Supplier sofaModuleRefreshExecutor(SofaModuleProperties sofaModuleProperties) { + @Conditional(OnVirtualThreadStartupDisableCondition.class) + public Supplier sofaModuleRefreshExecutor(SofaModuleProperties sofaModuleProperties) { int coreSize = (int) (SofaBootConstants.CPU_CORE * sofaModuleProperties.getParallelRefreshPoolSizeFactor()); long taskTimeout = sofaModuleProperties.getParallelRefreshTimeout(); long checkPeriod = sofaModuleProperties.getParallelRefreshCheckPeriod(); @@ -167,6 +172,17 @@ public Supplier sofaModuleRefreshExecutor(SofaModuleProperti TimeUnit.SECONDS); } + @Bean(SpringContextInstallStage.SOFA_MODULE_REFRESH_EXECUTOR_BEAN_NAME) + @ConditionalOnMissingBean(name = SpringContextInstallStage.SOFA_MODULE_REFRESH_EXECUTOR_BEAN_NAME) + @ConditionalOnProperty(value = "sofa.boot.isle.moduleStartUpParallel", havingValue = "true", matchIfMissing = true) + @Conditional(OnVirtualThreadStartupAvailableCondition.class) + public Supplier sofaModuleRefreshVirtualExecutor() { + return () -> { + LOGGER.info("Create SOFA module refresh virtual executor service"); + return SofaVirtualThreadFactory.ofExecutorService("sofa-module-refresh"); + }; + } + @Bean @ConditionalOnMissingBean public SofaModuleProfileChecker sofaModuleProfileChecker(SofaModuleProperties sofaModuleProperties) { diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/KubernetesRegistryConfiguration.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/KubernetesRegistryConfiguration.java new file mode 100644 index 000000000..8226cb3ac --- /dev/null +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/KubernetesRegistryConfiguration.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.autoconfigure.rpc; + +import com.alipay.sofa.boot.autoconfigure.condition.ConditionalOnSwitch; +import com.alipay.sofa.rpc.boot.config.KubernetesConfigurator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@ConditionalOnSwitch(value = "rpcKubernetesRegistry") +public class KubernetesRegistryConfiguration { + + @Bean + @ConditionalOnMissingBean + public KubernetesConfigurator kubernetesConfigurator() { + return new KubernetesConfigurator(); + } +} diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/PolarisRegistryConfiguration.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/PolarisRegistryConfiguration.java new file mode 100644 index 000000000..4ffef95f0 --- /dev/null +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/PolarisRegistryConfiguration.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.autoconfigure.rpc; + +import com.alipay.sofa.boot.autoconfigure.condition.ConditionalOnSwitch; +import com.alipay.sofa.rpc.boot.config.PolarisRegistryConfigurator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author chengming + * @version PolarisRegistryConfiguration.java, v 0.1 2024年02月27日 4:02 PM chengming + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnSwitch(value = "rpcPolarisRegistryConfiguration") +public class PolarisRegistryConfiguration { + + @Bean + @ConditionalOnMissingBean + public PolarisRegistryConfigurator polarisRegistryConfigurator() { + return new PolarisRegistryConfigurator(); + } +} diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/RegistryConfigurations.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/RegistryConfigurations.java index 64af355f2..316f3f6ef 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/RegistryConfigurations.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/RegistryConfigurations.java @@ -30,6 +30,8 @@ public static String[] registryConfigurationClass() { MulticastRegistryConfiguration.class.getName(), MeshRegistryConfiguration.class.getName(), ConsulRegistryConfiguration.class.getName(), - SofaRegistryConfiguration.class.getName() }; + SofaRegistryConfiguration.class.getName(), + PolarisRegistryConfiguration.class.getName(), + KubernetesRegistryConfiguration.class.getName() }; } } diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaBootRpcProperties.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaBootRpcProperties.java index deecd1e37..cbeaa6890 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaBootRpcProperties.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaBootRpcProperties.java @@ -342,6 +342,11 @@ public class SofaBootRpcProperties implements EnvironmentAware { private List providerRegisterBlackList; + /** + * 是否开启延时注册功能 + */ + private boolean enableDelayRegister; + public boolean isEnableAutoPublish() { return enableAutoPublish; } @@ -941,6 +946,14 @@ public void setProviderRegisterBlackList(List providerRegisterBlackList) this.providerRegisterBlackList = providerRegisterBlackList; } + public boolean isEnableDelayRegister() { + return enableDelayRegister; + } + + public void setEnableDelayRegister(boolean enableDelayRegister) { + this.enableDelayRegister = enableDelayRegister; + } + @Override public void setEnvironment(Environment environment) { this.environment = environment; diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfiguration.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfiguration.java index 3e3955f2f..d4cde6665 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfiguration.java @@ -24,6 +24,7 @@ import com.alipay.sofa.rpc.boot.config.RegistryConfigureProcessor; import com.alipay.sofa.rpc.boot.container.ConsumerConfigContainer; import com.alipay.sofa.rpc.boot.container.ProviderConfigContainer; +import com.alipay.sofa.rpc.boot.container.ProviderConfigDelayRegisterChecker; import com.alipay.sofa.rpc.boot.container.RegistryConfigContainer; import com.alipay.sofa.rpc.boot.container.ServerConfigContainer; import com.alipay.sofa.rpc.boot.context.RpcStartApplicationListener; @@ -55,6 +56,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * {@link EnableAutoConfiguration Auto-configuration} for sofa Rpc. @@ -72,12 +74,17 @@ public class SofaRpcAutoConfiguration { @Bean @ConditionalOnMissingBean - public ProviderConfigContainer providerConfigContainer(SofaBootRpcProperties sofaBootRpcProperties) { + public ProviderConfigContainer providerConfigContainer(SofaBootRpcProperties sofaBootRpcProperties, + ObjectProvider providerConfigDelayRegisterCheckers) { ProviderConfigContainer providerConfigContainer = new ProviderConfigContainer(); providerConfigContainer.setProviderRegisterWhiteList(sofaBootRpcProperties .getProviderRegisterWhiteList()); providerConfigContainer.setProviderRegisterBlackList(sofaBootRpcProperties .getProviderRegisterBlackList()); + providerConfigContainer.setProviderConfigDelayRegister(providerConfigDelayRegisterCheckers + .stream().collect(Collectors.toList())); + providerConfigContainer.setEnableDelayRegister(sofaBootRpcProperties + .isEnableDelayRegister()); return providerConfigContainer; } @@ -264,11 +271,9 @@ public SofaBootRpcStartListener sofaBootRpcStartListener(SofaBootRpcProperties s ObjectProvider faultToleranceConfigurator, ServerConfigContainer serverConfigContainer, RegistryConfigContainer registryConfigContainer) { - SofaBootRpcStartListener rpcStartListener = new SofaBootRpcStartListener( - providerConfigContainer, faultToleranceConfigurator.getIfUnique(), - serverConfigContainer, registryConfigContainer); - rpcStartListener.setLookoutCollectDisable(sofaBootRpcProperties.getLookoutCollectDisable()); - return rpcStartListener; + return new SofaBootRpcStartListener(providerConfigContainer, + faultToleranceConfigurator.getIfUnique(), serverConfigContainer, + registryConfigContainer); } @Bean diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfiguration.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfiguration.java index 6ff7e86c7..4227b2ec9 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfiguration.java @@ -17,10 +17,13 @@ package com.alipay.sofa.boot.autoconfigure.runtime; import com.alipay.sofa.boot.autoconfigure.condition.ConditionalOnSwitch; +import com.alipay.sofa.boot.autoconfigure.condition.OnVirtualThreadStartupAvailableCondition; +import com.alipay.sofa.boot.autoconfigure.condition.OnVirtualThreadStartupDisableCondition; import com.alipay.sofa.boot.constant.SofaBootConstants; import com.alipay.sofa.boot.log.SofaBootLoggerFactory; import com.alipay.sofa.common.thread.NamedThreadFactory; import com.alipay.sofa.common.thread.SofaThreadPoolExecutor; +import com.alipay.sofa.common.thread.virtual.SofaVirtualThreadFactory; import com.alipay.sofa.runtime.api.client.ReferenceClient; import com.alipay.sofa.runtime.api.client.ServiceClient; import com.alipay.sofa.runtime.async.AsyncInitMethodManager; @@ -54,12 +57,14 @@ import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.util.Assert; import java.util.HashSet; +import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -167,7 +172,8 @@ public static AsyncInitMethodManager asyncInitMethodManager() { @Bean(AsyncInitMethodManager.ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME) @ConditionalOnMissingBean(name = AsyncInitMethodManager.ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME) - public Supplier asyncInitMethodExecutor(SofaRuntimeProperties sofaRuntimeProperties) { + @Conditional(OnVirtualThreadStartupDisableCondition.class) + public Supplier asyncInitMethodExecutor(SofaRuntimeProperties sofaRuntimeProperties) { return ()-> { int coreSize = sofaRuntimeProperties.getAsyncInitExecutorCoreSize(); int maxSize = sofaRuntimeProperties.getAsyncInitExecutorMaxSize(); @@ -183,6 +189,16 @@ public Supplier asyncInitMethodExecutor(SofaRuntimePropertie }; } + @Bean(AsyncInitMethodManager.ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME) + @ConditionalOnMissingBean(name = AsyncInitMethodManager.ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME) + @Conditional(OnVirtualThreadStartupAvailableCondition.class) + public Supplier asyncInitMethodVirtualExecutor() { + return ()-> { + LOGGER.info("create async-init-bean virtual executor service"); + return SofaVirtualThreadFactory.ofExecutorService("async-init-bean"); + }; + } + @Bean @ConditionalOnMissingBean public static AsyncProxyBeanPostProcessor asyncProxyBeanPostProcessor(AsyncInitMethodManager asyncInitMethodManager) { diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupConditionTests.java b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupConditionTests.java new file mode 100644 index 000000000..5f55ed2df --- /dev/null +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/condition/OnVirtualThreadStartupConditionTests.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.autoconfigure.condition; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.StandardEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link OnVirtualThreadStartupDisableCondition} and {@link OnVirtualThreadStartupAvailableCondition}. + * + * @author huzijie + * @version OnVirtualThreadStartupConditionTests.java, v 0.1 2023年12月05日 5:04 PM huzijie Exp $ + */ +public class OnVirtualThreadStartupConditionTests { + + private ConfigurableApplicationContext context; + + private final ConfigurableEnvironment environment = new StandardEnvironment(); + + @Test + @EnabledOnJre(JRE.JAVA_17) + void checkJdk17Environment() { + load(OnVirtualThreadStartupConditionTests.OnVirtualThreadStartupConfiguration.class); + assertThat(this.context.containsBean("sampleA")).isTrue(); + assertThat(this.context.containsBean("sampleB")).isFalse(); + } + + @Test + @EnabledOnJre(JRE.JAVA_17) + void checkJdk17EnvironmentEnableProperty() { + load(OnVirtualThreadStartupConditionTests.OnVirtualThreadStartupConfiguration.class, + "sofa.boot.startup.threads.virtual.enabled=true"); + assertThat(this.context.containsBean("sampleA")).isTrue(); + assertThat(this.context.containsBean("sampleB")).isFalse(); + } + + @Test + @EnabledOnJre(JRE.JAVA_21) + void checkJdk21Environment() { + load(OnVirtualThreadStartupConditionTests.OnVirtualThreadStartupConfiguration.class); + assertThat(this.context.containsBean("sampleA")).isTrue(); + assertThat(this.context.containsBean("sampleB")).isFalse(); + } + + @Test + @EnabledOnJre(JRE.JAVA_21) + void checkJdk21EnvironmentEnableProperty() { + load(OnVirtualThreadStartupConditionTests.OnVirtualThreadStartupConfiguration.class, + "sofa.boot.startup.threads.virtual.enabled=true"); + assertThat(this.context.containsBean("sampleA")).isFalse(); + assertThat(this.context.containsBean("sampleB")).isTrue(); + } + + private void load(Class config, String... environment) { + TestPropertyValues.of(environment).and("spring.application.name=test") + .applyTo(this.environment); + this.context = new SpringApplicationBuilder(config).environment(this.environment) + .web(WebApplicationType.NONE).run(); + } + + @Configuration + static class OnVirtualThreadStartupConfiguration { + + @Bean + @Conditional(OnVirtualThreadStartupDisableCondition.class) + public String sampleA() { + return "sample"; + } + + @Bean + @Conditional(OnVirtualThreadStartupAvailableCondition.class) + public String sampleB() { + return "sample"; + } + } +} diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfigurationTests.java b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfigurationTests.java index 8ed49b60f..d1aa6698a 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfigurationTests.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/isle/SofaModuleAutoConfigurationTests.java @@ -31,10 +31,14 @@ import com.alipay.sofa.boot.isle.stage.SpringContextInstallStage; import com.alipay.sofa.common.thread.SofaThreadPoolExecutor; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; @@ -183,4 +187,16 @@ void customModelCreatingStage() { assertThat(modelCreatingStage.isAllowModuleOverriding()).isTrue(); }); } + + @Test + @EnabledOnJre(JRE.JAVA_21) + public void useSofaModuleRefreshVirtualExecutor() { + this.contextRunner + .withPropertyValues("sofa.boot.startup.threads.virtual.enabled=true") + .run((context) -> { + ExecutorService threadPoolExecutor = (ExecutorService) context.getBean(Supplier.class, + SpringContextInstallStage.SOFA_MODULE_REFRESH_EXECUTOR_BEAN_NAME).get(); + assertThat(threadPoolExecutor).isNotInstanceOf(ThreadPoolExecutor.class); + }); + } } diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfigurationTests.java b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfigurationTests.java index 97cbeb7c7..8e8ac4cf1 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfigurationTests.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/rpc/SofaRpcAutoConfigurationTests.java @@ -21,6 +21,7 @@ import com.alipay.sofa.rpc.boot.config.FaultToleranceConfigurator; import com.alipay.sofa.rpc.boot.config.RegistryConfigureProcessor; import com.alipay.sofa.rpc.boot.container.ProviderConfigContainer; +import com.alipay.sofa.rpc.boot.container.ProviderConfigDelayRegisterChecker; import com.alipay.sofa.rpc.boot.container.RegistryConfigContainer; import com.alipay.sofa.rpc.boot.container.ServerConfigContainer; import com.alipay.sofa.rpc.boot.runtime.adapter.processor.ConsumerMockProcessor; @@ -383,6 +384,46 @@ void customProviderConfigContainer() { }); } + @Test + void customProviderConfigContainerForEnableDelayRegister() { + this.contextRunner.withPropertyValues( + "sofa.boot.rpc.enableDelayRegister=true"). + run(context -> { + ProviderConfigContainer providerConfigContainer = context.getBean(ProviderConfigContainer.class); + boolean enableDelayRegister = providerConfigContainer.isEnableDelayRegister(); + assertThat(enableDelayRegister).isTrue(); + }); + } + + @Test + void customProviderConfigContainerForProviderConfigDelayRegisterChecker() { + this.contextRunner.withUserConfiguration(ProviderConfigDelayRegisterCheckerConfiguration.class) + .run(context -> { + ProviderConfigContainer providerConfigContainer = context.getBean(ProviderConfigContainer.class); + List checkers = + providerConfigContainer.getProviderConfigDelayRegisterCheckerList(); + assertThat(checkers.size()).isEqualTo(1); + assertThat(checkers.get(0).allowRegister()).isTrue(); + }); + } + + @Configuration(proxyBeanMethods = false) + static class ProviderConfigDelayRegisterCheckerConfiguration { + + @Bean + public ProviderConfigDelayRegisterChecker firstChecker() { + return new FirstChecker(); + } + } + + static class FirstChecker implements ProviderConfigDelayRegisterChecker { + + @Override + public boolean allowRegister() { + return true; + } + } + @Configuration(proxyBeanMethods = false) static class CustomProviderConfigContainerConfiguration { @Bean diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfigurationTests.java b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfigurationTests.java index bb6f6d31e..c6ab92b71 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfigurationTests.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/test/java/com/alipay/sofa/boot/autoconfigure/runtime/SofaRuntimeAutoConfigurationTests.java @@ -28,10 +28,13 @@ import com.alipay.sofa.runtime.spring.RuntimeContextBeanFactoryPostProcessor; import com.alipay.sofa.runtime.spring.ServiceBeanFactoryPostProcessor; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.function.Supplier; @@ -119,10 +122,23 @@ public void customAsyncInitMethodManager() { .withPropertyValues("sofa.boot.runtime.asyncInitExecutorCoreSize=10") .withPropertyValues("sofa.boot.runtime.asyncInitExecutorMaxSize=10") .run((context) -> { - ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) context.getBean(Supplier.class, + ExecutorService threadPoolExecutor = (ExecutorService) context.getBean(Supplier.class, AsyncInitMethodManager.ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME).get(); - assertThat(threadPoolExecutor.getCorePoolSize()).isEqualTo(10); - assertThat(threadPoolExecutor.getMaximumPoolSize()).isEqualTo(10); + assertThat(threadPoolExecutor).isInstanceOf(ThreadPoolExecutor.class); + assertThat(((ThreadPoolExecutor) threadPoolExecutor).getCorePoolSize()).isEqualTo(10); + assertThat(((ThreadPoolExecutor) threadPoolExecutor).getMaximumPoolSize()).isEqualTo(10); + }); + } + + @Test + @EnabledOnJre(JRE.JAVA_21) + public void useAsyncInitMethodVirtualExecutor() { + this.contextRunner + .withPropertyValues("sofa.boot.startup.threads.virtual.enabled=true") + .run((context) -> { + ExecutorService threadPoolExecutor = (ExecutorService) context.getBean(Supplier.class, + AsyncInitMethodManager.ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME).get(); + assertThat(threadPoolExecutor).isNotInstanceOf(ThreadPoolExecutor.class); }); } } diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/pom.xml b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/pom.xml index 2f8c8105a..d4b27073f 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/pom.xml +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + ARK SOFA Boot ark-sofa-boot diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/SofaRuntimeContainer.java b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/SofaRuntimeContainer.java index b50e0c74c..248c37eac 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/SofaRuntimeContainer.java +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/SofaRuntimeContainer.java @@ -18,7 +18,6 @@ import com.alipay.sofa.runtime.spi.component.SofaRuntimeManager; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.util.Assert; @@ -34,7 +33,7 @@ * @author huzijie * @since 2.5.0 */ -public class SofaRuntimeContainer implements ApplicationContextAware, DisposableBean { +public class SofaRuntimeContainer implements ApplicationContextAware { private static final Map APPLICATION_CONTEXT_MAP = new ConcurrentHashMap<>(); @@ -104,10 +103,14 @@ public static void clear() { JVM_INVOKE_SERIALIZE_MAP.clear(); } - @Override - public void destroy() { + public static void destroy(ClassLoader contextClassLoader) { APPLICATION_CONTEXT_MAP.remove(contextClassLoader); - SOFA_RUNTIME_MANAGER_MAP.remove(contextClassLoader); + + SofaRuntimeManager sofaRuntimeManager = SOFA_RUNTIME_MANAGER_MAP.remove(contextClassLoader); + if (sofaRuntimeManager != null) { + sofaRuntimeManager.shutDownExternally(); + } + JVM_SERVICE_CACHE_MAP.remove(contextClassLoader); JVM_INVOKE_SERIALIZE_MAP.remove(contextClassLoader); } diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandler.java b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandler.java index 96631b62b..9bb596cf4 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandler.java +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandler.java @@ -42,13 +42,8 @@ private void doUninstallBiz(Biz biz) { // Remove dynamic JVM service cache DynamicJvmServiceProxyFinder.getInstance().afterBizUninstall(biz); - SofaRuntimeManager sofaRuntimeManager = SofaRuntimeContainer.getSofaRuntimeManager(biz - .getBizClassLoader()); + SofaRuntimeContainer.destroy(biz.getBizClassLoader()); - if (sofaRuntimeManager == null) { - throw new IllegalStateException("No SofaRuntimeManager match classLoader"); - } - sofaRuntimeManager.shutDownExternally(); } @Override diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceInvoker.java b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceInvoker.java index 9c826c519..dd04ec5f9 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceInvoker.java +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceInvoker.java @@ -40,24 +40,24 @@ */ public class DynamicJvmServiceInvoker extends ServiceProxy { - private static final Logger LOGGER = SofaBootLoggerFactory - .getLogger(DynamicJvmServiceInvoker.class); + private static final Logger LOGGER = SofaBootLoggerFactory + .getLogger(DynamicJvmServiceInvoker.class); - private final Contract contract; - private final Object targetService; - private final String bizIdentity; - private final ThreadLocal clientClassloader = new ThreadLocal<>(); - private final boolean serialize; + private final Contract contract; + private final Object targetService; + private final String bizIdentity; + private final ClassLoader clientClassloader; + private final boolean serialize; - static protected final String TOSTRING_METHOD = "toString"; - static protected final String EQUALS_METHOD = "equals"; - static protected final String HASHCODE_METHOD = "hashCode"; + static protected final String TOSTRING_METHOD = "toString"; + static protected final String EQUALS_METHOD = "equals"; + static protected final String HASHCODE_METHOD = "hashCode"; public DynamicJvmServiceInvoker(ClassLoader clientClassloader, ClassLoader serviceClassLoader, Object targetService, Contract contract, String bizIdentity, boolean serialize) { super(serviceClassLoader); - this.clientClassloader.set(clientClassloader); + this.clientClassloader = clientClassloader; this.targetService = targetService; this.contract = contract; this.bizIdentity = bizIdentity; @@ -103,10 +103,10 @@ protected Object doInvoke(MethodInvocation invocation) throws Throwable { } } else { Object[] arguments = (Object[]) hessianTransport(targetArguments, null); - Class[] argumentTypes = (Class[]) hessianTransport(oldArgumentTypes, null); + Class[] argumentTypes = (Class[]) hessianTransport(oldArgumentTypes, null); transformMethod = getTargetMethod(targetMethod, argumentTypes); Object retVal = transformMethod.invoke(targetService, arguments); - return hessianTransport(retVal, getClientClassloader()); + return hessianTransport(retVal, clientClassloader); } } catch (InvocationTargetException ex) { throw ex.getTargetException(); @@ -114,7 +114,6 @@ protected Object doInvoke(MethodInvocation invocation) throws Throwable { if (DynamicJvmServiceProxyFinder.getInstance().getBizManagerService() != null) { ReplayContext.clearPlaceHolder(); } - clearClientClassloader(); } } @@ -136,18 +135,6 @@ public Class getInterfaceType() { return contract.getInterfaceType(); } - public ClassLoader getClientClassloader() { - return clientClassloader.get(); - } - - public void setClientClassloader(ClassLoader clientClassloader) { - this.clientClassloader.set(clientClassloader); - } - - public void clearClientClassloader() { - this.clientClassloader.remove(); - } - private Method getTargetMethod(Method method, Class[] argumentTypes) { try { return targetService.getClass().getMethod(method.getName(), argumentTypes); diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceProxyFinder.java b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceProxyFinder.java index 15cff0e61..917c17960 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceProxyFinder.java +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/main/java/com/alipay/sofa/boot/ark/invoke/DynamicJvmServiceProxyFinder.java @@ -251,13 +251,8 @@ public static Biz getBiz(SofaRuntimeManager sofaRuntimeManager) { if (getInstance().bizManagerService == null) { return null; } - - for (Biz biz : getInstance().bizManagerService.getBizInOrder()) { - if (biz.getBizClassLoader().equals(sofaRuntimeManager.getAppClassLoader())) { - return biz; - } - } - return null; + return getInstance().bizManagerService.getBizByClassLoader(sofaRuntimeManager + .getAppClassLoader()); } public boolean isHasFinishStartup() { diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/MockBizManagerService.java b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/MockBizManagerService.java index b9594d204..a481a9c1f 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/MockBizManagerService.java +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/MockBizManagerService.java @@ -65,6 +65,11 @@ public Biz getBizByIdentity(String s) { @Override public Biz getBizByClassLoader(ClassLoader classLoader) { + for (Biz biz : bizList) { + if (biz.getBizClassLoader().equals(classLoader)) { + return biz; + } + } return null; } diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/SofaRuntimeContainerTests.java b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/SofaRuntimeContainerTests.java index 97897b19c..e9958117d 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/SofaRuntimeContainerTests.java +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/SofaRuntimeContainerTests.java @@ -93,7 +93,7 @@ public void destroy() throws Exception { assertThat(SofaRuntimeContainer.getApplicationContext(classLoaderA)).isEqualTo( genericApplicationContext); - sofaRuntimeContainer.destroy(); + sofaRuntimeContainer.destroy(classLoaderA); assertThat(SofaRuntimeContainer.getApplicationContext(classLoaderA)).isNull(); } diff --git a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandlerTests.java b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandlerTests.java index 0950b2be3..be3c975de 100644 --- a/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandlerTests.java +++ b/sofa-boot-project/sofa-boot-core/ark-sofa-boot/src/test/java/com/alipay/sofa/boot/ark/handler/SofaBizUninstallEventHandlerTests.java @@ -27,7 +27,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.support.GenericApplicationContext; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -50,14 +49,6 @@ public void clear() { SofaRuntimeContainer.clear(); } - @Test - public void noSofaRuntimeManager() { - MockBiz mockBiz = new MockBiz(); - assertThatThrownBy(() -> sofaBizUninstallEventHandler.handleEvent(new BeforeBizStopEvent(mockBiz))) - .isInstanceOf(IllegalStateException.class) - .hasMessage("No SofaRuntimeManager match classLoader"); - } - @Test public void shutDown() { SofaRuntimeContainer sofaRuntimeContainer = new SofaRuntimeContainer(sofaRuntimeManager); diff --git a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/pom.xml b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/pom.xml index 60ef2f432..d9bb7fe76 100644 --- a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/pom.xml +++ b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + Isle SOFABoot isle-sofa-boot diff --git a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoader.java b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoader.java index 2c4201555..cbeb918de 100644 --- a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoader.java +++ b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoader.java @@ -87,6 +87,7 @@ public void loadSpringContext(DeploymentDescriptor deployment, ClassLoader classLoader = deployment.getClassLoader(); SofaDefaultListableBeanFactory beanFactory = SofaSpringContextSupport.createBeanFactory(classLoader, this::createBeanFactory); + beanFactory.setId(deployment.getModuleName()); SofaGenericApplicationContext context = SofaSpringContextSupport.createApplicationContext(beanFactory, this::createApplicationContext); if (startupReporter != null) { diff --git a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/stage/SpringContextInstallStage.java b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/stage/SpringContextInstallStage.java index 85688f383..bdf0bfcf8 100644 --- a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/stage/SpringContextInstallStage.java +++ b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/boot/isle/stage/SpringContextInstallStage.java @@ -184,36 +184,13 @@ private void doRefreshSpringContextParallel() { /** * Refresh all {@link ApplicationContext} recursively */ - private void refreshRecursively(DeploymentDescriptor deployment, - CountDownLatch latch, List> futures) { - futures.add(moduleRefreshExecutorService.submit(() -> { - String oldName = Thread.currentThread().getName(); - try { - Thread.currentThread().setName( - "sofa-module-refresh-" + deployment.getModuleName()); - if (deployment.isSpringPowered() && !application.getFailed().contains(deployment)) { - refreshAndCollectCost(deployment); - } - DependencyTree.Entry entry = application - .getDeployRegistry().getEntry(deployment.getModuleName()); - if (entry != null && entry.getDependsOnMe() != null) { - for (DependencyTree.Entry child : entry - .getDependsOnMe()) { - child.getDependencies().remove(entry); - if (child.getDependencies().size() == 0) { - refreshRecursively(child.get(), latch, futures); - } - } - } - } catch (Throwable t) { - LOGGER.error(ErrorCode.convert("01-11002", deployment.getName()), t); - throw new RuntimeException(ErrorCode.convert("01-11002", deployment.getName()), - t); - } finally { - latch.countDown(); - Thread.currentThread().setName(oldName); - } - })); + private void refreshRecursively(DeploymentDescriptor deployment, CountDownLatch latch, + List> futures) { + // if interrupted, moduleRefreshExecutorService will be null; + if (moduleRefreshExecutorService != null) { + futures.add(moduleRefreshExecutorService.submit(new AsyncSpringContextRunnable( + deployment, latch, futures))); + } } protected void refreshAndCollectCost(DeploymentDescriptor deployment) { @@ -288,4 +265,46 @@ public String getName() { public int getOrder() { return 20000; } + + private class AsyncSpringContextRunnable implements Runnable { + private final DeploymentDescriptor deployment; + private final CountDownLatch latch; + private final List> futures; + + public AsyncSpringContextRunnable(DeploymentDescriptor deployment, CountDownLatch latch, + List> futures) { + this.deployment = deployment; + this.latch = latch; + this.futures = futures; + } + + @Override + public void run() { + String oldName = Thread.currentThread().getName(); + try { + Thread.currentThread().setName("sofa-module-refresh-" + deployment.getModuleName()); + if (deployment.isSpringPowered() && !application.getFailed().contains(deployment)) { + SpringContextInstallStage.this.refreshAndCollectCost(deployment); + } + DependencyTree.Entry entry = application + .getDeployRegistry().getEntry(deployment.getModuleName()); + if (entry != null && entry.getDependsOnMe() != null) { + for (DependencyTree.Entry child : entry + .getDependsOnMe()) { + child.getDependencies().remove(entry); + if (child.getDependencies().size() == 0) { + SpringContextInstallStage.this.refreshRecursively(child.get(), latch, + futures); + } + } + } + } catch (Throwable t) { + LOGGER.error(ErrorCode.convert("01-11002", deployment.getName()), t); + throw new RuntimeException(ErrorCode.convert("01-11002", deployment.getName()), t); + } finally { + latch.countDown(); + Thread.currentThread().setName(oldName); + } + } + } } diff --git a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/test/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoaderTests.java b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/test/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoaderTests.java index 71973ee17..b2d9ba0c0 100644 --- a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/test/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoaderTests.java +++ b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/test/java/com/alipay/sofa/boot/isle/loader/DynamicSpringContextLoaderTests.java @@ -28,7 +28,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionReader; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; @@ -103,10 +102,11 @@ void loadSpringContext() { assertThat(applicationContext.getEnvironment().getActiveProfiles()).containsOnly("test"); applicationContext.refresh(); - DefaultListableBeanFactory autowireCapableBeanFactory = (DefaultListableBeanFactory) applicationContext + SofaDefaultListableBeanFactory autowireCapableBeanFactory = (SofaDefaultListableBeanFactory) applicationContext .getAutowireCapableBeanFactory(); assertThat(autowireCapableBeanFactory.isAllowBeanDefinitionOverriding()).isTrue(); assertThat(autowireCapableBeanFactory).isInstanceOf(SofaDefaultListableBeanFactory.class); + assertThat(autowireCapableBeanFactory.getId()).isEqualTo("test"); } @Test diff --git a/sofa-boot-project/sofa-boot-core/pom.xml b/sofa-boot-project/sofa-boot-core/pom.xml index 24c6793b6..38e51be79 100644 --- a/sofa-boot-project/sofa-boot-core/pom.xml +++ b/sofa-boot-project/sofa-boot-core/pom.xml @@ -20,6 +20,7 @@ test-sofa-boot + SOFABoot Core sofa-boot-core diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/pom.xml b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/pom.xml index de01faef1..3989fc756 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/pom.xml +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/pom.xml @@ -9,11 +9,13 @@ 4.0.0 + RPC SOFABoot rpc-sofa-boot ${basedir}/../../.. 0.0.3 + 3.17.3 @@ -152,7 +154,7 @@ protobuf-maven-plugin 0.5.1 - com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protobuf.protoc.version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} build/generated/source/proto/test/java diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/common/RpcThreadPoolMonitor.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/common/RpcThreadPoolMonitor.java index 8d654f2db..8d5c85e05 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/common/RpcThreadPoolMonitor.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/common/RpcThreadPoolMonitor.java @@ -82,6 +82,8 @@ public void start() { StringBuilder sb = new StringBuilder(); sb.append("coreSize:" + threadPoolExecutor.getCorePoolSize() + ","); sb.append("maxPoolSize:" + threadPoolExecutor.getMaximumPoolSize() + ","); + sb.append("queueRemainingSize:" + + threadPoolExecutor.getQueue().remainingCapacity() + ","); sb.append("keepAliveTime:" + threadPoolExecutor.getKeepAliveTime(TimeUnit.MILLISECONDS) + "\n"); diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/KubernetesConfigurator.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/KubernetesConfigurator.java new file mode 100644 index 000000000..fb70abead --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/KubernetesConfigurator.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 com.alipay.sofa.rpc.boot.config; + +import com.alipay.sofa.rpc.boot.common.RegistryParseUtil; +import com.alipay.sofa.rpc.config.RegistryConfig; + +import java.util.Map; + +/** + * Kubernetes配置 + *

+ * com.alipay.sofa.rpc.registry.address=kubernetes://kubernetes.default.svc:8848?k1=v1 + */ +public class KubernetesConfigurator implements RegistryConfigureProcessor { + + public KubernetesConfigurator() { + } + + @Override + public RegistryConfig buildFromAddress(String address) { + String kubernetesAddress = RegistryParseUtil.parseAddress(address, + SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_KUBERNETES); + Map map = RegistryParseUtil.parseParam(address, + SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_KUBERNETES); + + return new RegistryConfig().setAddress(kubernetesAddress).setParameters(map) + .setProtocol(SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_KUBERNETES); + } + + @Override + public String registryType() { + return SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_KUBERNETES; + } + +} diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/PolarisRegistryConfigurator.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/PolarisRegistryConfigurator.java new file mode 100644 index 000000000..3c735f87a --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/PolarisRegistryConfigurator.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 com.alipay.sofa.rpc.boot.config; + +import com.alipay.sofa.rpc.boot.common.RegistryParseUtil; +import com.alipay.sofa.rpc.config.RegistryConfig; + +import java.util.Map; + +/** + * @author chengming + * @version PolarisRegistryConfigurator.java, v 0.1 2024年02月27日 3:52 PM chengming + */ +public class PolarisRegistryConfigurator implements RegistryConfigureProcessor { + + public PolarisRegistryConfigurator() { + } + + @Override + public RegistryConfig buildFromAddress(String address) { + String polarisAddress = RegistryParseUtil.parseAddress(address, + SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_POLARIS); + Map map = RegistryParseUtil.parseParam(address, + SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_POLARIS); + + return new RegistryConfig().setAddress(polarisAddress).setParameters(map) + .setProtocol(SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_POLARIS); + } + + @Override + public String registryType() { + return SofaBootRpcConfigConstants.REGISTRY_PROTOCOL_POLARIS; + } +} diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcConfigConstants.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcConfigConstants.java index 8ab776b03..477fc4eeb 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcConfigConstants.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcConfigConstants.java @@ -69,6 +69,10 @@ public class SofaBootRpcConfigConstants { //@since 5.5.2 public static final String REGISTRY_PROTOCOL_SOFA = "sofa"; + public static final String REGISTRY_PROTOCOL_POLARIS = "polaris"; + + public static final String REGISTRY_PROTOCOL_KUBERNETES = "kubernetes"; + /* server */ public static final String RPC_PROTOCOL_BOLT = "bolt"; public static final String RPC_PROTOCOL_REST = "rest"; diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ProviderConfigContainer.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ProviderConfigContainer.java index 582ba3fcf..6ace6f752 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ProviderConfigContainer.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ProviderConfigContainer.java @@ -16,6 +16,8 @@ */ package com.alipay.sofa.rpc.boot.container; +import com.alipay.sofa.boot.constant.SofaBootConstants; +import com.alipay.sofa.common.thread.SofaScheduledThreadPoolExecutor; import com.alipay.sofa.rpc.boot.config.SofaBootRpcConfigConstants; import com.alipay.sofa.rpc.boot.log.SofaBootRpcLoggerFactory; import com.alipay.sofa.rpc.boot.runtime.binding.RpcBinding; @@ -33,6 +35,7 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; /** * ProviderConfig持有者.维护编程界面级别的RPC组件。 @@ -58,6 +61,21 @@ public class ProviderConfigContainer { private final ConcurrentMap RPC_SERVICE_CONTAINER = new ConcurrentHashMap( 256); + /** + * 用来延时发布的线程池 + */ + private SofaScheduledThreadPoolExecutor scheduledExecutorService; + + /** + * 用于判断是否允许延迟服务发布至注册中心的扩展点、需要所有拓展点都通过 + */ + private List providerConfigDelayRegisterCheckerList; + + /** + * 是否开启延时加载,兼容老逻辑 + */ + private boolean enableDelayRegister; + /** * 增加 ProviderConfig * @@ -127,32 +145,60 @@ public Collection getAllProviderConfig() { */ public void publishAllProviderConfig() { for (ProviderConfig providerConfig : getAllProviderConfig()) { + int delay = providerConfig.getDelay(); + if (enableDelayRegister && delay > 0) { + // 根据延时时间异步去注册中心注册服务 + if (scheduledExecutorService == null) { + scheduledExecutorService = new SofaScheduledThreadPoolExecutor(1, "rpc-provider-delay-register", + SofaBootConstants.SOFA_BOOT_SPACE_NAME); + } + scheduledExecutorService.schedule(() -> doDelayPublishProviderConfig(providerConfig, + providerConfigDelayRegisterCheckerList), delay, TimeUnit.MILLISECONDS); + } else { + // 没有配置延时加载则直接去注册中心注册服务 + doPublishProviderConfig(providerConfig); + } + } + } - ServerConfig serverConfig = (ServerConfig) providerConfig.getServer().get(0); - if (!serverConfig.getProtocol().equalsIgnoreCase( - SofaBootRpcConfigConstants.RPC_PROTOCOL_DUBBO)) { - if (allowProviderRegister(providerConfig)) { - providerConfig.setRegister(true); - } else { - LOGGER.info("Provider will not register: [{}]", providerConfig.buildKey()); + private void doDelayPublishProviderConfig(ProviderConfig providerConfig, + List providerConfigDelayRegisterCheckerList) { + for (ProviderConfigDelayRegisterChecker providerConfigDelayRegisterChecker : providerConfigDelayRegisterCheckerList) { + if (!providerConfigDelayRegisterChecker.allowRegister()) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("service publish failed, interfaceId [{}], please check.", + providerConfig.getInterfaceId()); } + return; + } + } + doPublishProviderConfig(providerConfig); + } - List registrys = providerConfig.getRegistry(); - for (RegistryConfig registryConfig : registrys) { + private void doPublishProviderConfig(ProviderConfig providerConfig) { + ServerConfig serverConfig = (ServerConfig) providerConfig.getServer().get(0); + if (!serverConfig.getProtocol().equalsIgnoreCase( + SofaBootRpcConfigConstants.RPC_PROTOCOL_DUBBO)) { + if (allowProviderRegister(providerConfig)) { + providerConfig.setRegister(true); + } else { + LOGGER.info("Provider will not register: [{}]", providerConfig.buildKey()); + } - Registry registry = RegistryFactory.getRegistry(registryConfig); - registry.init(); - registry.start(); + List registrys = providerConfig.getRegistry(); + for (RegistryConfig registryConfig : registrys) { - registry.register(providerConfig); + Registry registry = RegistryFactory.getRegistry(registryConfig); + registry.init(); + registry.start(); - if (LOGGER.isInfoEnabled()) { - LOGGER.info("service published. interfaceId[" - + providerConfig.getInterfaceId() + "]; protocol[" - + serverConfig.getProtocol() + "]"); - } - } + registry.register(providerConfig); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("service published. interfaceId[" + + providerConfig.getInterfaceId() + "]; protocol[" + + serverConfig.getProtocol() + "]"); + } } } } @@ -257,4 +303,20 @@ public List getProviderRegisterWhiteList() { public List getProviderRegisterBlackList() { return providerRegisterBlackList; } + + public void setProviderConfigDelayRegister(List providerConfigDelayRegisterCheckerList) { + this.providerConfigDelayRegisterCheckerList = providerConfigDelayRegisterCheckerList; + } + + public List getProviderConfigDelayRegisterCheckerList() { + return providerConfigDelayRegisterCheckerList; + } + + public void setEnableDelayRegister(boolean enableDelayRegister) { + this.enableDelayRegister = enableDelayRegister; + } + + public boolean isEnableDelayRegister() { + return enableDelayRegister; + } } diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ProviderConfigDelayRegisterChecker.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ProviderConfigDelayRegisterChecker.java new file mode 100644 index 000000000..a4746235b --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ProviderConfigDelayRegisterChecker.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.rpc.boot.container; + +/** + * 用于判断是否允许延迟服务发布至注册中心的扩展点 + */ +public interface ProviderConfigDelayRegisterChecker { + + /** + * 是否允许注册服务到注册中心 + * + * @return + */ + boolean allowRegister(); +} diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ServerConfigContainer.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ServerConfigContainer.java index 5f6ac49ec..539dc2d70 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ServerConfigContainer.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/container/ServerConfigContainer.java @@ -40,6 +40,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** @@ -275,9 +276,14 @@ private void startCustomThreadPoolMonitor() { } else { customThreadPoolMonitor.setPoolName(pool.getThreadPoolName()); } - customThreadPoolMonitor.setThreadPoolExecutor(pool.getExecutor()); - customThreadPoolMonitor.start(); - poolNames.add(pool.getThreadPoolName()); + + Executor tmp = pool.getUserExecutor(); + if (tmp instanceof ThreadPoolExecutor) { + customThreadPoolMonitor.setThreadPoolExecutor((ThreadPoolExecutor) pool + .getUserExecutor()); + customThreadPoolMonitor.start(); + poolNames.add(pool.getThreadPoolName()); + } } } } diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/context/SofaBootRpcStartListener.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/context/SofaBootRpcStartListener.java index 14d1511df..8e5d59be1 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/context/SofaBootRpcStartListener.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/context/SofaBootRpcStartListener.java @@ -16,14 +16,12 @@ */ package com.alipay.sofa.rpc.boot.context; -import com.alipay.sofa.rpc.boot.common.SofaBootRpcParserUtil; import com.alipay.sofa.rpc.boot.config.FaultToleranceConfigurator; import com.alipay.sofa.rpc.boot.container.ProviderConfigContainer; import com.alipay.sofa.rpc.boot.container.RegistryConfigContainer; import com.alipay.sofa.rpc.boot.container.ServerConfigContainer; import com.alipay.sofa.rpc.boot.context.event.SofaBootRpcStartEvent; import com.alipay.sofa.rpc.config.ProviderConfig; -import com.alipay.sofa.rpc.event.LookoutSubscriber; import org.springframework.context.ApplicationListener; import org.springframework.util.CollectionUtils; @@ -46,8 +44,6 @@ public class SofaBootRpcStartListener implements ApplicationListener 4.0.0 + Runtime SOFABoot runtime-sofa-boot diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitMethodManager.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitMethodManager.java index 05b803d18..b93ed6ec6 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitMethodManager.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitMethodManager.java @@ -30,8 +30,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -49,7 +49,7 @@ public class AsyncInitMethodManager implements PriorityOrdered, public static final String ASYNC_INIT_METHOD_NAME = "async-init-method-name"; - private final AtomicReference threadPoolExecutorRef = new AtomicReference<>(); + private final AtomicReference executorServiceRef = new AtomicReference<>(); private final Map> asyncInitBeanNameMap = new ConcurrentHashMap<>(); @@ -77,20 +77,20 @@ public void setApplicationContext(ApplicationContext applicationContext) throws } public void submitTask(Runnable runnable) { - if (threadPoolExecutorRef.get() == null) { - ThreadPoolExecutor threadPoolExecutor = createAsyncExecutor(); - boolean success = threadPoolExecutorRef.compareAndSet(null, threadPoolExecutor); + if (executorServiceRef.get() == null) { + ExecutorService executorService = createAsyncExecutorService(); + boolean success = executorServiceRef.compareAndSet(null, executorService); if (!success) { - threadPoolExecutor.shutdown(); + executorService.shutdown(); } } - Future future = threadPoolExecutorRef.get().submit(runnable); + Future future = executorServiceRef.get().submit(runnable); futures.add(future); } - private ThreadPoolExecutor createAsyncExecutor() { - return (ThreadPoolExecutor) applicationContext.getBean( - ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME, Supplier.class).get(); + private ExecutorService createAsyncExecutorService() { + return (ExecutorService) applicationContext.getBean(ASYNC_INIT_METHOD_EXECUTOR_BEAN_NAME, + Supplier.class).get(); } void ensureAsyncTasksFinish() { @@ -105,9 +105,9 @@ void ensureAsyncTasksFinish() { startUpFinish = true; futures.clear(); asyncInitBeanNameMap.clear(); - if (threadPoolExecutorRef.get() != null) { - threadPoolExecutorRef.get().shutdown(); - threadPoolExecutorRef.set(null); + if (executorServiceRef.get() != null) { + executorServiceRef.get().shutdown(); + executorServiceRef.set(null); } } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitializeBeanMethodInvoker.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitializeBeanMethodInvoker.java index 850a1c55f..632361d99 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitializeBeanMethodInvoker.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/async/AsyncInitializeBeanMethodInvoker.java @@ -75,28 +75,15 @@ public Object invoke(final MethodInvocation invocation) throws Throwable { if (!isAsyncCalled && methodName.equals(asyncMethodName)) { isAsyncCalled = true; isAsyncCalling = true; - asyncInitMethodManager.submitTask(() -> { - try { - long startTime = System.currentTimeMillis(); - invocation.getMethod().invoke(targetObject, invocation.getArguments()); - LOGGER.info("{}({}) {} method execute {}dms.", targetObject - .getClass().getName(), beanName, methodName, (System - .currentTimeMillis() - startTime)); - } catch (Throwable e) { - throw new RuntimeException(e); - } finally { - asyncMethodFinish(); - } - }); + asyncInitMethodManager.submitTask(new AsyncBeanInitRunnable(invocation)); return null; } if (isAsyncCalling) { long startTime = System.currentTimeMillis(); initCountDownLatch.await(); - LOGGER.info("{}({}) {} method wait {}ms.", - targetObject.getClass().getName(), beanName, methodName, - (System.currentTimeMillis() - startTime)); + LOGGER.info("{}({}) {} method wait {}ms.", targetObject.getClass().getName(), beanName, + methodName, (System.currentTimeMillis() - startTime)); } return invocation.getMethod().invoke(targetObject, invocation.getArguments()); } @@ -105,4 +92,28 @@ void asyncMethodFinish() { this.initCountDownLatch.countDown(); this.isAsyncCalling = false; } + + private class AsyncBeanInitRunnable implements Runnable { + + private final MethodInvocation invocation; + + public AsyncBeanInitRunnable(MethodInvocation invocation) { + this.invocation = invocation; + } + + @Override + public void run() { + try { + long startTime = System.currentTimeMillis(); + invocation.getMethod().invoke(targetObject, invocation.getArguments()); + LOGGER.info("{}({}) {} method execute {}dms.", targetObject.getClass().getName(), + beanName, invocation.getMethod().getName(), + (System.currentTimeMillis() - startTime)); + } catch (Throwable e) { + throw new RuntimeException(e); + } finally { + AsyncInitializeBeanMethodInvoker.this.asyncMethodFinish(); + } + } + } } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/filter/JvmFilterHolder.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/filter/JvmFilterHolder.java index 03ffd8d5d..0fd5c9971 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/filter/JvmFilterHolder.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/filter/JvmFilterHolder.java @@ -18,9 +18,8 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -31,7 +30,7 @@ */ public class JvmFilterHolder { - private final List JVM_FILTERS = new ArrayList<>(); + private final List JVM_FILTERS = new CopyOnWriteArrayList<>(); private final AtomicBoolean FILTERS_SORTED = new AtomicBoolean(false); @@ -56,8 +55,8 @@ public List getJvmFilters() { } public static boolean beforeInvoking(JvmFilterContext context) { - List filters = Collections.unmodifiableList(context.getSofaRuntimeContext() - .getJvmFilterHolder().getJvmFilters()); + List filters = context.getSofaRuntimeContext().getJvmFilterHolder() + .getJvmFilters(); for (JvmFilter filter : filters) { if (!filter.before(context)) { return false; @@ -67,8 +66,8 @@ public static boolean beforeInvoking(JvmFilterContext context) { } public static boolean afterInvoking(JvmFilterContext context) { - List filters = Collections.unmodifiableList(context.getSofaRuntimeContext() - .getJvmFilterHolder().getJvmFilters()); + List filters = context.getSofaRuntimeContext().getJvmFilterHolder() + .getJvmFilters(); for (int i = filters.size() - 1; i >= 0; --i) { if (!filters.get(i).after(context)) { return false; diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ClientFactoryImpl.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ClientFactoryImpl.java index 05a35e54a..af7d35e46 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ClientFactoryImpl.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ClientFactoryImpl.java @@ -40,6 +40,11 @@ public void registerClient(Class clientType, Object clientInstance) { clients.put(clientType, clientInstance); } + @Override + public void destroy() { + clients.clear(); + } + @SuppressWarnings("unchecked") @Override public T getClient(Class clazz) { diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ComponentManagerImpl.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ComponentManagerImpl.java index 33aaabe51..5197980bd 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ComponentManagerImpl.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/ComponentManagerImpl.java @@ -187,7 +187,7 @@ private ComponentInfo doRegister(ComponentInfo ci) { ci.register(); } catch (Throwable t) { LOGGER.error(ErrorCode.convert("01-03003", ci.getName()), t); - return null; + throw new ServiceRuntimeException(ErrorCode.convert("01-03003", ci.getName())); } LOGGER.info("Registering component: {}", ci.getName()); @@ -209,6 +209,7 @@ private ComponentInfo doRegister(ComponentInfo ci) { } catch (Throwable t) { ci.exception(new Exception(t)); LOGGER.error(ErrorCode.convert("01-03004", ci.getName()), t); + throw new ServiceRuntimeException(ErrorCode.convert("01-03004", ci.getName())); } return ci; diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/StandardSofaRuntimeManager.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/StandardSofaRuntimeManager.java index ee25fce0d..59d882964 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/StandardSofaRuntimeManager.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/impl/StandardSofaRuntimeManager.java @@ -112,11 +112,13 @@ public void shutdown() throws ServiceRuntimeException { @Override public void shutDownExternally() throws ServiceRuntimeException { try { + clientFactoryInternal.destroy(); AbstractApplicationContext applicationContext = (AbstractApplicationContext) rootApplicationContext; // only need shutdown when root context is active if (applicationContext.isActive()) { applicationContext.close(); } + rootApplicationContext = null; appClassLoader = null; } catch (Throwable throwable) { throw new ServiceRuntimeException(ErrorCode.convert("01-03100"), throwable); diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/client/ClientFactoryInternal.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/client/ClientFactoryInternal.java index 0d709d2f4..9634ac82f 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/client/ClientFactoryInternal.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/client/ClientFactoryInternal.java @@ -32,4 +32,6 @@ public interface ClientFactoryInternal extends ClientFactory { * @param clientInstance client instance */ void registerClient(Class clientType, Object clientInstance); + + void destroy(); } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java index 55b17e607..de5c6d236 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java @@ -56,18 +56,18 @@ public class ReferenceAnnotationBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, PriorityOrdered { - private static final Logger LOGGER = SofaBootLoggerFactory - .getLogger(ReferenceAnnotationBeanPostProcessor.class); + private static final Logger LOGGER = SofaBootLoggerFactory + .getLogger(ReferenceAnnotationBeanPostProcessor.class); - private final SofaRuntimeContext sofaRuntimeContext; + private final SofaRuntimeContext sofaRuntimeContext; - private final BindingAdapterFactory bindingAdapterFactory; + private final BindingAdapterFactory bindingAdapterFactory; - private final BindingConverterFactory bindingConverterFactory; + private final BindingConverterFactory bindingConverterFactory; - private ApplicationContext applicationContext; + private ApplicationContext applicationContext; - private AnnotationWrapper annotationWrapper; + private final ThreadLocal> annotationWrapper = new ThreadLocal<>(); /** * To construct a ReferenceAnnotationBeanPostProcessor via a Spring Bean @@ -97,7 +97,10 @@ private void processSofaReference(final Object bean, String beanName) { return; } - sofaReferenceAnnotation = annotationWrapper.wrap(sofaReferenceAnnotation); + if (annotationWrapper.get() == null) { + annotationWrapper.set(createAnnotationWrapper()); + } + sofaReferenceAnnotation = annotationWrapper.get().wrap(sofaReferenceAnnotation); Class interfaceType = sofaReferenceAnnotation.interfaceType(); if (interfaceType.equals(void.class)) { @@ -130,7 +133,10 @@ private void processSofaReference(final Object bean, String beanName) { return; } - sofaReferenceAnnotation = annotationWrapper.wrap(sofaReferenceAnnotation); + if (annotationWrapper.get() == null) { + annotationWrapper.set(createAnnotationWrapper()); + } + sofaReferenceAnnotation = annotationWrapper.get().wrap(sofaReferenceAnnotation); Class interfaceType = sofaReferenceAnnotation.interfaceType(); if (interfaceType.equals(void.class)) { @@ -182,8 +188,12 @@ public int getOrder() { @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; - this.annotationWrapper = AnnotationWrapper.create(SofaReference.class) - .withEnvironment(applicationContext.getEnvironment()) + this.annotationWrapper.set(createAnnotationWrapper()); + } + + private AnnotationWrapper createAnnotationWrapper() { + return AnnotationWrapper.create(SofaReference.class) + .withEnvironment(this.applicationContext.getEnvironment()) .withBinder(DefaultPlaceHolderBinder.INSTANCE); } } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java index 7619090ac..cff89f37f 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/ServiceBeanFactoryPostProcessor.java @@ -35,6 +35,7 @@ import com.alipay.sofa.runtime.spi.service.BindingConverter; import com.alipay.sofa.runtime.spi.service.BindingConverterContext; import com.alipay.sofa.runtime.spi.service.BindingConverterFactory; +import com.alipay.sofa.boot.spring.parameter.LocalVariableTableParameterNameDiscoverer; import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; import com.alipay.sofa.runtime.spring.bean.SofaParameterNameDiscoverer; import com.alipay.sofa.runtime.spring.factory.ReferenceFactoryBean; @@ -52,6 +53,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; @@ -60,8 +62,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ScannedGenericBeanDefinition; import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.NativeDetector; import org.springframework.core.Ordered; import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.PrioritizedParameterNameDiscoverer; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.StandardMethodMetadata; @@ -113,6 +117,11 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) if (parameterNameDiscoverer == null) { parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); } + // keep compatible for second jars + if (parameterNameDiscoverer instanceof PrioritizedParameterNameDiscoverer prioritizedParameterNameDiscoverer + && !NativeDetector.inNativeImage()) { + prioritizedParameterNameDiscoverer.addDiscoverer(new LocalVariableTableParameterNameDiscoverer()); + } ((AbstractAutowireCapableBeanFactory) beanFactory) .setParameterNameDiscoverer(new SofaParameterNameDiscoverer(parameterNameDiscoverer, referenceAnnotationWrapper)); } @@ -258,7 +267,9 @@ private void doGenerateSofaReferenceDefinition(BeanDefinition beanDefinition, if (!registry.containsBeanDefinition(referenceId)) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); builder.getRawBeanDefinition().setScope(beanDefinition.getScope()); - builder.getRawBeanDefinition().setLazyInit(beanDefinition.isLazyInit()); + if (((AbstractBeanDefinition) beanDefinition).getLazyInit() != null) { + builder.getRawBeanDefinition().setLazyInit(beanDefinition.isLazyInit()); + } builder.getRawBeanDefinition().setBeanClass(ReferenceFactoryBean.class); builder.addAutowiredProperty(AbstractContractDefinitionParser.SOFA_RUNTIME_CONTEXT); builder @@ -323,7 +334,9 @@ private void generateSofaServiceDefinition(String beanId, SofaService sofaServic if (!registry.containsBeanDefinition(serviceId)) { builder.getRawBeanDefinition().setScope(beanDefinition.getScope()); - builder.setLazyInit(beanDefinition.isLazyInit()); + if (((AbstractBeanDefinition) beanDefinition).getLazyInit() != null) { + builder.setLazyInit(beanDefinition.isLazyInit()); + } builder.getRawBeanDefinition().setBeanClass(ServiceFactoryBean.class); builder.addAutowiredProperty(AbstractContractDefinitionParser.SOFA_RUNTIME_CONTEXT); builder diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/bean/LocalVariableTableParameterNameDiscoverer.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/bean/LocalVariableTableParameterNameDiscoverer.java new file mode 100644 index 000000000..81e8b82b7 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spring/bean/LocalVariableTableParameterNameDiscoverer.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.spring.bean; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.asm.ClassReader; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Label; +import org.springframework.asm.MethodVisitor; +import org.springframework.asm.Opcodes; +import org.springframework.asm.SpringAsmInfo; +import org.springframework.asm.Type; +import org.springframework.core.BridgeMethodResolver; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Deprecate in spring, fork codes for compatible + */ +@Deprecated +public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer { + + private static final Log logger = LogFactory + .getLog(LocalVariableTableParameterNameDiscoverer.class); + + // marker object for classes that do not have any debug info + private static final Map NO_DEBUG_INFO_MAP = Collections + .emptyMap(); + + // the cache uses a nested index (value is a map) to keep the top level cache relatively small in size + private final Map, Map> parameterNamesCache = new ConcurrentHashMap<>( + 32); + + @Override + @Nullable + public String[] getParameterNames(Method method) { + Method originalMethod = BridgeMethodResolver.findBridgedMethod(method); + return doGetParameterNames(originalMethod); + } + + @Override + @Nullable + public String[] getParameterNames(Constructor ctor) { + return doGetParameterNames(ctor); + } + + @Nullable + private String[] doGetParameterNames(Executable executable) { + Class declaringClass = executable.getDeclaringClass(); + Map map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass); + return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null); + } + + /** + * Inspects the target class. + *

Exceptions will be logged, and a marker map returned to indicate the + * lack of debug information. + */ + private Map inspectClass(Class clazz) { + InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz)); + if (is == null) { + // We couldn't load the class file, which is not fatal as it + // simply means this method of discovering parameter names won't work. + if (logger.isDebugEnabled()) { + logger.debug("Cannot find '.class' file for class [" + clazz + + "] - unable to determine constructor/method parameter names"); + } + return NO_DEBUG_INFO_MAP; + } + // We cannot use try-with-resources here for the InputStream, since we have + // custom handling of the close() method in a finally-block. + try { + ClassReader classReader = new ClassReader(is); + Map map = new ConcurrentHashMap<>(32); + classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0); + if (logger.isWarnEnabled()) { + logger + .warn("Using deprecated '-debug' fallback for parameter name resolution. Compile the " + + "affected code with '-parameters' instead or avoid its introspection: " + + clazz.getName()); + } + return map; + } catch (IOException ex) { + if (logger.isDebugEnabled()) { + logger.debug("Exception thrown while reading '.class' file for class [" + clazz + + "] - unable to determine constructor/method parameter names", ex); + } + } catch (IllegalArgumentException ex) { + if (logger.isDebugEnabled()) { + logger + .debug( + "ASM ClassReader failed to parse class file [" + + clazz + + "], probably due to a new Java class file version that isn't supported yet " + + "- unable to determine constructor/method parameter names", ex); + } + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + return NO_DEBUG_INFO_MAP; + } + + /** + * Helper class that inspects all methods and constructors and then + * attempts to find the parameter names for the given {@link Executable}. + */ + private static class ParameterNameDiscoveringVisitor extends ClassVisitor { + + private static final String STATIC_CLASS_INIT = ""; + + private final Class clazz; + + private final Map executableMap; + + public ParameterNameDiscoveringVisitor(Class clazz, + Map executableMap) { + super(SpringAsmInfo.ASM_VERSION); + this.clazz = clazz; + this.executableMap = executableMap; + } + + @Override + @Nullable + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { + // exclude synthetic + bridged && static class initialization + if (!isSyntheticOrBridged(access) && !STATIC_CLASS_INIT.equals(name)) { + return new LocalVariableTableVisitor(this.clazz, this.executableMap, name, desc, + isStatic(access)); + } + return null; + } + + private static boolean isSyntheticOrBridged(int access) { + return (((access & Opcodes.ACC_SYNTHETIC) | (access & Opcodes.ACC_BRIDGE)) > 0); + } + + private static boolean isStatic(int access) { + return ((access & Opcodes.ACC_STATIC) > 0); + } + } + + private static class LocalVariableTableVisitor extends MethodVisitor { + + private static final String CONSTRUCTOR = ""; + + private final Class clazz; + + private final Map executableMap; + + private final String name; + + private final Type[] args; + + private final String[] parameterNames; + + private final boolean isStatic; + + private boolean hasLvtInfo = false; + + /* + * The nth entry contains the slot index of the LVT table entry holding the + * argument name for the nth parameter. + */ + private final int[] lvtSlotIndex; + + public LocalVariableTableVisitor(Class clazz, Map map, + String name, String desc, boolean isStatic) { + super(SpringAsmInfo.ASM_VERSION); + this.clazz = clazz; + this.executableMap = map; + this.name = name; + this.args = Type.getArgumentTypes(desc); + this.parameterNames = new String[this.args.length]; + this.isStatic = isStatic; + this.lvtSlotIndex = computeLvtSlotIndices(isStatic, this.args); + } + + @Override + public void visitLocalVariable(String name, String description, String signature, + Label start, Label end, int index) { + this.hasLvtInfo = true; + for (int i = 0; i < this.lvtSlotIndex.length; i++) { + if (this.lvtSlotIndex[i] == index) { + this.parameterNames[i] = name; + } + } + } + + @Override + public void visitEnd() { + if (this.hasLvtInfo || (this.isStatic && this.parameterNames.length == 0)) { + // visitLocalVariable will never be called for static no args methods + // which doesn't use any local variables. + // This means that hasLvtInfo could be false for that kind of methods + // even if the class has local variable info. + this.executableMap.put(resolveExecutable(), this.parameterNames); + } + } + + private Executable resolveExecutable() { + ClassLoader loader = this.clazz.getClassLoader(); + Class[] argTypes = new Class[this.args.length]; + for (int i = 0; i < this.args.length; i++) { + argTypes[i] = ClassUtils.resolveClassName(this.args[i].getClassName(), loader); + } + try { + if (CONSTRUCTOR.equals(this.name)) { + return this.clazz.getDeclaredConstructor(argTypes); + } + return this.clazz.getDeclaredMethod(this.name, argTypes); + } catch (NoSuchMethodException ex) { + throw new IllegalStateException( + "Method [" + + this.name + + "] was discovered in the .class file but cannot be resolved in the class object", + ex); + } + } + + private static int[] computeLvtSlotIndices(boolean isStatic, Type[] paramTypes) { + int[] lvtIndex = new int[paramTypes.length]; + int nextIndex = (isStatic ? 0 : 1); + for (int i = 0; i < paramTypes.length; i++) { + lvtIndex[i] = nextIndex; + if (isWideType(paramTypes[i])) { + nextIndex += 2; + } else { + nextIndex++; + } + } + return lvtIndex; + } + + private static boolean isWideType(Type aType) { + // float is not a wide type + return (aType == Type.LONG_TYPE || aType == Type.DOUBLE_TYPE); + } + } + +} diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/impl/ComponentManagerImplTests.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/impl/ComponentManagerImplTests.java index 378a1d4eb..f08906718 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/impl/ComponentManagerImplTests.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/impl/ComponentManagerImplTests.java @@ -138,9 +138,12 @@ public void registerException(CapturedOutput capturedOutput) { componentInfoA = new DemoComponent("A"); componentInfoA.setRegisterException(true); - assertThat(componentManager.registerAndGet(componentInfoA)).isNull(); - assertThat(capturedOutput.getOut()).contains("01-03003"); - assertThat(capturedOutput.getOut()).contains(componentInfoA.getName().toString()); + try { + assertThat(componentManager.registerAndGet(componentInfoA)).isNull(); + } catch (ServiceRuntimeException e) { + assertThat(capturedOutput.getOut()).contains("01-03003"); + assertThat(capturedOutput.getOut()).contains(componentInfoA.getName().toString()); + } } @Test @@ -148,10 +151,14 @@ public void resolveException(CapturedOutput capturedOutput) { componentInfoA = new DemoComponent("A"); componentInfoA.setResolveException(true); - assertThat(componentManager.registerAndGet(componentInfoA)).isEqualTo(componentInfoA); - assertThat(componentInfoA.isHealthy().isHealthy()).isFalse(); - assertThat(capturedOutput.getOut()).contains("01-03004"); - assertThat(capturedOutput.getOut()).contains(componentInfoA.getName().toString()); + try { + assertThat(componentManager.registerAndGet(componentInfoA)).isEqualTo(componentInfoA); + + } catch (ServiceRuntimeException e) { + assertThat(componentInfoA.isHealthy().isHealthy()).isFalse(); + assertThat(capturedOutput.getOut()).contains("01-03004"); + assertThat(capturedOutput.getOut()).contains(componentInfoA.getName().toString()); + } } @Test diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/pom.xml b/sofa-boot-project/sofa-boot-core/test-sofa-boot/pom.xml index 241a24151..bf0276d5e 100644 --- a/sofa-boot-project/sofa-boot-core/test-sofa-boot/pom.xml +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + Test SOFABoot test-sofa-boot diff --git a/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/pom.xml b/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/pom.xml index 4732710d7..54e153b92 100644 --- a/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/pom.xml +++ b/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + Tracer SOFABoot tracer-sofa-boot diff --git a/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/src/test/java/com/alipay/sofa/boot/tracer/kafka/KafkaConsumerFactoryBeanPostProcessorTests.java b/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/src/test/java/com/alipay/sofa/boot/tracer/kafka/KafkaConsumerFactoryBeanPostProcessorTests.java index e0cb622b5..37bdb0234 100644 --- a/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/src/test/java/com/alipay/sofa/boot/tracer/kafka/KafkaConsumerFactoryBeanPostProcessorTests.java +++ b/sofa-boot-project/sofa-boot-core/tracer-sofa-boot/src/test/java/com/alipay/sofa/boot/tracer/kafka/KafkaConsumerFactoryBeanPostProcessorTests.java @@ -21,6 +21,8 @@ import org.junit.jupiter.api.Test; import org.springframework.kafka.core.ConsumerFactory; +import java.util.Properties; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -65,7 +67,8 @@ public void skipTransformedKafkaConsumerFactory() { static class EmptyConsumerFactory implements ConsumerFactory { @Override - public Consumer createConsumer(String s, String s1, String s2) { + public Consumer createConsumer(String groupId, String clientIdPrefix, + String clientIdSuffix, Properties properties) { return null; } diff --git a/sofa-boot-project/sofa-boot-parent/pom.xml b/sofa-boot-project/sofa-boot-parent/pom.xml index 53bffcc6c..7279f3476 100644 --- a/sofa-boot-project/sofa-boot-parent/pom.xml +++ b/sofa-boot-project/sofa-boot-parent/pom.xml @@ -11,6 +11,7 @@ 4.0.0 pom + SOFABoot Parent sofa-boot-parent diff --git a/sofa-boot-project/sofa-boot-starters/actuator-sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/actuator-sofa-boot-starter/pom.xml index bd8feca33..cf45415ef 100644 --- a/sofa-boot-project/sofa-boot-starters/actuator-sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/actuator-sofa-boot-starter/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + Actuator SOFABoot Starter actuator-sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-starters/ark-sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/ark-sofa-boot-starter/pom.xml index 3ad06a212..23de55ae7 100644 --- a/sofa-boot-project/sofa-boot-starters/ark-sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/ark-sofa-boot-starter/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + Ark SOFABoot Starter ark-sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-starters/isle-sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/isle-sofa-boot-starter/pom.xml index 2ff37c91b..a377b4aba 100644 --- a/sofa-boot-project/sofa-boot-starters/isle-sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/isle-sofa-boot-starter/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + Isle SOFABoot Starter isle-sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-starters/pom.xml b/sofa-boot-project/sofa-boot-starters/pom.xml index e81ee05ae..6591f3c77 100644 --- a/sofa-boot-project/sofa-boot-starters/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/pom.xml @@ -10,6 +10,7 @@ 4.0.0 + SOFABoot Starters sofa-boot-starters pom diff --git a/sofa-boot-project/sofa-boot-starters/rpc-sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/rpc-sofa-boot-starter/pom.xml index 3f34faca2..4eccd4e9e 100644 --- a/sofa-boot-project/sofa-boot-starters/rpc-sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/rpc-sofa-boot-starter/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + RPC SOFABoot Starter rpc-sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-starters/runtime-sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/runtime-sofa-boot-starter/pom.xml index 58974b93a..c56eccdbd 100644 --- a/sofa-boot-project/sofa-boot-starters/runtime-sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/runtime-sofa-boot-starter/pom.xml @@ -26,6 +26,7 @@ 4.0.0 + Runtime SOFABoot Starter runtime-sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-starters/sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/sofa-boot-starter/pom.xml index 7ccfbf574..d38f89f3a 100644 --- a/sofa-boot-project/sofa-boot-starters/sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/sofa-boot-starter/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + SOFABoot Starter sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-starters/test-sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/test-sofa-boot-starter/pom.xml index 0e947f08b..064b79e93 100644 --- a/sofa-boot-project/sofa-boot-starters/test-sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/test-sofa-boot-starter/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + SOFABoot Test Starter test-sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-starters/tracer-sofa-boot-starter/pom.xml b/sofa-boot-project/sofa-boot-starters/tracer-sofa-boot-starter/pom.xml index c93cbc838..8fd107644 100644 --- a/sofa-boot-project/sofa-boot-starters/tracer-sofa-boot-starter/pom.xml +++ b/sofa-boot-project/sofa-boot-starters/tracer-sofa-boot-starter/pom.xml @@ -9,6 +9,7 @@ 4.0.0 + SOFABoot Tracer Starter tracer-sofa-boot-starter diff --git a/sofa-boot-project/sofa-boot-tools/pom.xml b/sofa-boot-project/sofa-boot-tools/pom.xml index 891f60712..1d62f4b98 100644 --- a/sofa-boot-project/sofa-boot-tools/pom.xml +++ b/sofa-boot-project/sofa-boot-tools/pom.xml @@ -11,6 +11,7 @@ 4.0.0 pom + SOFABoot Tools sofa-boot-tools diff --git a/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/build.gradle b/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/build.gradle index 1767ca116..8e688b364 100644 --- a/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/build.gradle +++ b/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/build.gradle @@ -15,11 +15,11 @@ repositories { dependencies { implementation localGroovy() implementation gradleApi() - implementation 'org.springframework.boot:spring-boot-gradle-plugin:3.1.2' - implementation 'org.springframework.boot:spring-boot-loader-tools:3.0.2' - implementation 'io.spring.gradle:dependency-management-plugin:1.1.0' + implementation 'org.springframework.boot:spring-boot-gradle-plugin:3.5.6' + implementation 'org.springframework.boot:spring-boot-loader-tools:3.5.6' + implementation 'io.spring.gradle:dependency-management-plugin:1.1.7' implementation "org.apache.commons:commons-compress:1.19" - implementation "org.springframework:spring-core:6.0.2" + implementation "org.springframework:spring-core:6.2.11" testImplementation 'junit:junit:4.13.1' testImplementation gradleTestKit() } diff --git a/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties index afef0da98..11edbffc2 100644 --- a/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties +++ b/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/pom.xml b/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/pom.xml index 1bbc12c5a..0b4dc9853 100644 --- a/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/pom.xml +++ b/sofa-boot-project/sofa-boot-tools/sofa-boot-gradle-plugin/pom.xml @@ -20,12 +20,12 @@ org.springframework.boot spring-boot-gradle-plugin - 3.1.2 + 3.5.6 io.spring.gradle dependency-management-plugin - 1.1.0 + 1.1.7 org.apache.commons diff --git a/sofa-boot-project/sofa-boot/pom.xml b/sofa-boot-project/sofa-boot/pom.xml index 2b160377f..505c3df7a 100644 --- a/sofa-boot-project/sofa-boot/pom.xml +++ b/sofa-boot-project/sofa-boot/pom.xml @@ -10,6 +10,7 @@ 4.0.0 + SOFA Boot sofa-boot @@ -45,6 +46,13 @@ logback-classic test + + + org.mockito + mockito-inline + test + + diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/Initializer/AutoModuleExportApplicationContextInitializer.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/Initializer/AutoModuleExportApplicationContextInitializer.java new file mode 100644 index 000000000..3460426c9 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/Initializer/AutoModuleExportApplicationContextInitializer.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 com.alipay.sofa.boot.Initializer; + +import com.alipay.sofa.boot.util.ModuleUtil; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * @author huazhongming + * @since 4.4.0 + */ +public class AutoModuleExportApplicationContextInitializer + implements + ApplicationContextInitializer { + + private static final String AUTO_MODULE_JDK_ENABLE_KEY = "sofa.boot.auto.module.export.jdk.enable"; + private static final String AUTO_MODULE_ALL_ENABLE_KEY = "sofa.boot.auto.module.export.all.enable"; + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + if (isEnable(applicationContext, AUTO_MODULE_ALL_ENABLE_KEY, "false")) { + ModuleUtil.exportAllModulePackageToAll(); + } else if (isEnable(applicationContext, AUTO_MODULE_JDK_ENABLE_KEY, "true")) { + ModuleUtil.exportAllJDKModulePackageToAll(); + } + } + + protected boolean isEnable(ConfigurableApplicationContext applicationContext, String key, + String defaultValue) { + String switchStr = applicationContext.getEnvironment().getProperty(key, defaultValue); + return Boolean.parseBoolean(switchStr); + } +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreator.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreator.java new file mode 100644 index 000000000..cc8f1f8ec --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreator.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 com.alipay.sofa.boot.aop.framework.autoproxy; + +import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.PatternMatchUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Extension for {@link BeanNameAutoProxyCreator} to support exclude specify bean names. + * + * @author huzijie + * @version ExcludeBeanNameAutoProxyCreator.java, v 0.1 2024年01月04日 4:24 PM huzijie Exp $ + */ +public class ExcludeBeanNameAutoProxyCreator extends BeanNameAutoProxyCreator { + + @Nullable + private List excludeBeanNames; + + /** + * Set the names of the beans that should not automatically get wrapped with proxies. + * A name can specify a prefix to match by ending with "*", e.g. "myBean,tx*" + * will match the bean named "myBean" and all beans whose name start with "tx". + *

NOTE: In case of a FactoryBean, only the objects created by the + * FactoryBean will get proxied. This default behavior applies as of Spring 2.0. + * If you intend to proxy a FactoryBean instance itself (a rare use case, but + * Spring 1.2's default behavior), specify the bean name of the FactoryBean + * including the factory-bean prefix "&": e.g. "&myFactoryBean". + * @see org.springframework.beans.factory.FactoryBean + * @see org.springframework.beans.factory.BeanFactory#FACTORY_BEAN_PREFIX + */ + public void setExcludeBeanNames(String... beanNames) { + Assert.notEmpty(beanNames, "'excludeBeanNames' must not be empty"); + this.excludeBeanNames = new ArrayList<>(beanNames.length); + for (String mappedName : beanNames) { + this.excludeBeanNames.add(mappedName.strip()); + } + } + + @Override + protected boolean isMatch(String beanName, String mappedName) { + return super.isMatch(beanName, mappedName) && !isExcluded(beanName); + } + + private boolean isExcluded(String beanName) { + if (excludeBeanNames != null) { + for (String mappedName : this.excludeBeanNames) { + if (PatternMatchUtils.simpleMatch(mappedName, beanName)) { + return true; + } + } + } + return false; + } +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/constant/SofaBootConstants.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/constant/SofaBootConstants.java index 824488b0d..e5e304ec7 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/constant/SofaBootConstants.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/constant/SofaBootConstants.java @@ -88,7 +88,7 @@ public class SofaBootConstants { /** * Default exposure web endpoint list */ - public static final String SOFA_DEFAULT_ENDPOINTS_WEB_EXPOSURE_VALUE = "info,health,readiness,startup,beans,components,rpc,isle"; + public static final String SOFA_DEFAULT_ENDPOINTS_WEB_EXPOSURE_VALUE = "info,health,readiness,startup,beans,components,rpc,isle,threadpool,sbom"; /** * CPU core diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/context/SofaDefaultListableBeanFactory.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/context/SofaDefaultListableBeanFactory.java index 9e8b9ee4e..18cfad609 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/context/SofaDefaultListableBeanFactory.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/context/SofaDefaultListableBeanFactory.java @@ -17,6 +17,7 @@ package com.alipay.sofa.boot.context; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.util.ObjectUtils; /** * Default Implementation of {@link DefaultListableBeanFactory} in SOFABoot framework. @@ -28,4 +29,14 @@ * @since 4.0.0 */ public class SofaDefaultListableBeanFactory extends DefaultListableBeanFactory { + + private String id = ObjectUtils.identityToString(this); + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return this.id; + } } diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/spring/parameter/LocalVariableTableParameterNameDiscoverer.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/spring/parameter/LocalVariableTableParameterNameDiscoverer.java new file mode 100644 index 000000000..4d4867aa3 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/spring/parameter/LocalVariableTableParameterNameDiscoverer.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.spring.parameter; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.asm.ClassReader; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.Label; +import org.springframework.asm.MethodVisitor; +import org.springframework.asm.Opcodes; +import org.springframework.asm.SpringAsmInfo; +import org.springframework.asm.Type; +import org.springframework.core.BridgeMethodResolver; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Deprecate in spring, fork codes for compatible + */ +@Deprecated +public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer { + + private static final Log logger = LogFactory + .getLog(LocalVariableTableParameterNameDiscoverer.class); + + // marker object for classes that do not have any debug info + private static final Map NO_DEBUG_INFO_MAP = Collections + .emptyMap(); + + // the cache uses a nested index (value is a map) to keep the top level cache relatively small in size + private final Map, Map> parameterNamesCache = new ConcurrentHashMap<>( + 32); + + @Override + @Nullable + public String[] getParameterNames(Method method) { + Method originalMethod = BridgeMethodResolver.findBridgedMethod(method); + return doGetParameterNames(originalMethod); + } + + @Override + @Nullable + public String[] getParameterNames(Constructor ctor) { + return doGetParameterNames(ctor); + } + + @Nullable + private String[] doGetParameterNames(Executable executable) { + Class declaringClass = executable.getDeclaringClass(); + Map map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass); + return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null); + } + + /** + * Inspects the target class. + *

Exceptions will be logged, and a marker map returned to indicate the + * lack of debug information. + */ + private Map inspectClass(Class clazz) { + InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz)); + if (is == null) { + // We couldn't load the class file, which is not fatal as it + // simply means this method of discovering parameter names won't work. + if (logger.isDebugEnabled()) { + logger.debug("Cannot find '.class' file for class [" + clazz + + "] - unable to determine constructor/method parameter names"); + } + return NO_DEBUG_INFO_MAP; + } + // We cannot use try-with-resources here for the InputStream, since we have + // custom handling of the close() method in a finally-block. + try { + ClassReader classReader = new ClassReader(is); + Map map = new ConcurrentHashMap<>(32); + classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0); + if (logger.isWarnEnabled()) { + logger + .warn("Using deprecated '-debug' fallback for parameter name resolution. Compile the " + + "affected code with '-parameters' instead or avoid its introspection: " + + clazz.getName()); + } + return map; + } catch (IOException ex) { + if (logger.isDebugEnabled()) { + logger.debug("Exception thrown while reading '.class' file for class [" + clazz + + "] - unable to determine constructor/method parameter names", ex); + } + } catch (IllegalArgumentException ex) { + if (logger.isDebugEnabled()) { + logger + .debug( + "ASM ClassReader failed to parse class file [" + + clazz + + "], probably due to a new Java class file version that isn't supported yet " + + "- unable to determine constructor/method parameter names", ex); + } + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + return NO_DEBUG_INFO_MAP; + } + + /** + * Helper class that inspects all methods and constructors and then + * attempts to find the parameter names for the given {@link Executable}. + */ + private static class ParameterNameDiscoveringVisitor extends ClassVisitor { + + private static final String STATIC_CLASS_INIT = ""; + + private final Class clazz; + + private final Map executableMap; + + public ParameterNameDiscoveringVisitor(Class clazz, + Map executableMap) { + super(SpringAsmInfo.ASM_VERSION); + this.clazz = clazz; + this.executableMap = executableMap; + } + + @Override + @Nullable + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { + // exclude synthetic + bridged && static class initialization + if (!isSyntheticOrBridged(access) && !STATIC_CLASS_INIT.equals(name)) { + return new LocalVariableTableVisitor(this.clazz, this.executableMap, name, desc, + isStatic(access)); + } + return null; + } + + private static boolean isSyntheticOrBridged(int access) { + return (((access & Opcodes.ACC_SYNTHETIC) | (access & Opcodes.ACC_BRIDGE)) > 0); + } + + private static boolean isStatic(int access) { + return ((access & Opcodes.ACC_STATIC) > 0); + } + } + + private static class LocalVariableTableVisitor extends MethodVisitor { + + private static final String CONSTRUCTOR = ""; + + private final Class clazz; + + private final Map executableMap; + + private final String name; + + private final Type[] args; + + private final String[] parameterNames; + + private final boolean isStatic; + + private boolean hasLvtInfo = false; + + /* + * The nth entry contains the slot index of the LVT table entry holding the + * argument name for the nth parameter. + */ + private final int[] lvtSlotIndex; + + public LocalVariableTableVisitor(Class clazz, Map map, + String name, String desc, boolean isStatic) { + super(SpringAsmInfo.ASM_VERSION); + this.clazz = clazz; + this.executableMap = map; + this.name = name; + this.args = Type.getArgumentTypes(desc); + this.parameterNames = new String[this.args.length]; + this.isStatic = isStatic; + this.lvtSlotIndex = computeLvtSlotIndices(isStatic, this.args); + } + + @Override + public void visitLocalVariable(String name, String description, String signature, + Label start, Label end, int index) { + this.hasLvtInfo = true; + for (int i = 0; i < this.lvtSlotIndex.length; i++) { + if (this.lvtSlotIndex[i] == index) { + this.parameterNames[i] = name; + } + } + } + + @Override + public void visitEnd() { + if (this.hasLvtInfo || (this.isStatic && this.parameterNames.length == 0)) { + // visitLocalVariable will never be called for static no args methods + // which doesn't use any local variables. + // This means that hasLvtInfo could be false for that kind of methods + // even if the class has local variable info. + this.executableMap.put(resolveExecutable(), this.parameterNames); + } + } + + private Executable resolveExecutable() { + ClassLoader loader = this.clazz.getClassLoader(); + Class[] argTypes = new Class[this.args.length]; + for (int i = 0; i < this.args.length; i++) { + argTypes[i] = ClassUtils.resolveClassName(this.args[i].getClassName(), loader); + } + try { + if (CONSTRUCTOR.equals(this.name)) { + return this.clazz.getDeclaredConstructor(argTypes); + } + return this.clazz.getDeclaredMethod(this.name, argTypes); + } catch (NoSuchMethodException ex) { + throw new IllegalStateException( + "Method [" + + this.name + + "] was discovered in the .class file but cannot be resolved in the class object", + ex); + } + } + + private static int[] computeLvtSlotIndices(boolean isStatic, Type[] paramTypes) { + int[] lvtIndex = new int[paramTypes.length]; + int nextIndex = (isStatic ? 0 : 1); + for (int i = 0; i < paramTypes.length; i++) { + lvtIndex[i] = nextIndex; + if (isWideType(paramTypes[i])) { + nextIndex += 2; + } else { + nextIndex++; + } + } + return lvtIndex; + } + + private static boolean isWideType(Type aType) { + // float is not a wide type + return (aType == Type.LONG_TYPE || aType == Type.DOUBLE_TYPE); + } + } + +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupReporter.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupReporter.java index f8414699b..88e49d820 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupReporter.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupReporter.java @@ -53,6 +53,10 @@ public class StartupReporter { public static final String SPRING_CONTEXT_BEAN_FACTORY_POST_PROCESSOR = "spring.context.bean-factory.post-process"; + public static final String SPRING_BEAN_POST_PROCESSOR = "spring.context.beans.post-process"; + + public static final String SPRING_CONFIG_CLASSES_ENHANCE = "spring.context.config-classes.enhance"; + public static final Collection SPRING_BEAN_INSTANTIATE_TYPES = Set .of(SPRING_BEANS_INSTANTIATE, SPRING_BEANS_SMART_INSTANTIATE); @@ -61,6 +65,10 @@ public class StartupReporter { .of(SPRING_CONTEXT_BEANDEF_REGISTRY_POST_PROCESSOR, SPRING_CONTEXT_BEAN_FACTORY_POST_PROCESSOR); + public static final Collection SPRING_CONFIG_CLASSES_ENHANCE_TYPES = Set + .of(SPRING_CONFIG_CLASSES_ENHANCE, + SPRING_BEAN_POST_PROCESSOR); + private final StartupStaticsModel startupStaticsModel; private final List beanStatCustomizers; @@ -216,7 +224,9 @@ public List generateBeanStats(ConfigurableApplicationContext context) private boolean filterBeanInitializeByCost(BeanStat beanStat) { String name = beanStat.getType(); - if (SPRING_BEAN_INSTANTIATE_TYPES.contains(name)) { + if (SPRING_BEAN_INSTANTIATE_TYPES.contains(name) + || SPRING_CONTEXT_POST_PROCESSOR_TYPES.contains(name) + || SPRING_CONFIG_CLASSES_ENHANCE_TYPES.contains(name)) { return beanStat.getCost() >= costThreshold; } else { return true; diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupSpringApplicationRunListener.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupSpringApplicationRunListener.java index 8d0298673..97508485d 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupSpringApplicationRunListener.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/startup/StartupSpringApplicationRunListener.java @@ -42,29 +42,24 @@ */ public class StartupSpringApplicationRunListener implements SpringApplicationRunListener, Ordered { - private final SpringApplication application; + private final SpringApplication application; - private final String[] args; + private final String[] args; - private final StartupReporter startupReporter; + private final StartupReporter startupReporter; - private final ApplicationStartup userApplicationStartup; + private BaseStat jvmStartingStage; - private BufferingApplicationStartup applicationStartup; + private BaseStat environmentPrepareStage; - private BaseStat jvmStartingStage; + private ChildrenStat applicationContextPrepareStage; - private BaseStat environmentPrepareStage; - - private ChildrenStat applicationContextPrepareStage; - - private BaseStat applicationContextLoadStage; + private BaseStat applicationContextLoadStage; public StartupSpringApplicationRunListener(SpringApplication springApplication, String[] args) { this.application = springApplication; this.args = args; this.startupReporter = new StartupReporter(); - this.userApplicationStartup = springApplication.getApplicationStartup(); } @Override @@ -84,16 +79,12 @@ public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, environmentPrepareStage.setEndTime(System.currentTimeMillis()); startupReporter.setAppName(environment.getProperty(SofaBootConstants.APP_NAME_KEY)); startupReporter.bindToStartupReporter(environment); + bootstrapContext.register(StartupReporter.class, key -> startupReporter); // create BufferingApplicationStartup if user not custom. + ApplicationStartup userApplicationStartup = application.getApplicationStartup(); if (ApplicationStartup.DEFAULT == userApplicationStartup || userApplicationStartup == null) { - this.applicationStartup = new BufferingApplicationStartup(startupReporter.getBufferSize()); - } else if (userApplicationStartup instanceof BufferingApplicationStartup userApplicationStartup) { - // use user custom BufferingApplicationStartup - this.applicationStartup = userApplicationStartup; - } else { - // disable startup static when user custom other type ApplicationStartup; - this.applicationStartup = null; + application.setApplicationStartup(new BufferingApplicationStartup(startupReporter.getBufferSize())); } } @@ -109,9 +100,6 @@ public void contextPrepared(ConfigurableApplicationContext context) { applicationContextPrepareStage.setChildren(new ArrayList<>(baseStatList)); baseStatList.clear(); } - if (applicationStartup != null) { - context.setApplicationStartup(applicationStartup); - } } @Override @@ -156,7 +144,7 @@ public void started(ConfigurableApplicationContext context, Duration timeTaken) @Override public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; + return Ordered.LOWEST_PRECEDENCE - 10; } private String getStartedMessage(Environment environment, Duration timeTakenToStartup) { diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java new file mode 100644 index 000000000..1e19b81cd --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.util; + +import com.alipay.sofa.boot.log.SofaBootLoggerFactory; +import org.slf4j.Logger; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @author huazhongming + * @since 4.4.0 + */ +public class ModuleUtil { + + private static final Logger LOGGER = SofaBootLoggerFactory + .getLogger(ModuleUtil.class); + + private static final MethodHandle implAddOpensToAllUnnamed; + + private static final MethodHandle implAddOpens; + + private static final MethodHandle implAddExportsToAllUnnamed; + + private static final MethodHandle implAddExports; + + private static final Map nameToModules; + + private static final AtomicBoolean isExported = new AtomicBoolean(false); + + static { + implAddOpensToAllUnnamed = createModuleMethodHandle("implAddOpensToAllUnnamed", + String.class); + implAddOpens = createModuleMethodHandle("implAddOpens", String.class); + implAddExportsToAllUnnamed = createModuleMethodHandle("implAddExportsToAllUnnamed", + String.class); + implAddExports = createModuleMethodHandle("implAddExports", String.class); + nameToModules = getNameToModule(); + } + + /** + * Export all JDK module packages to all. + */ + public static void exportAllJDKModulePackageToAll() { + try { + if (isExported.compareAndSet(false,true) && nameToModules != null) { + nameToModules.forEach((name, module) -> module.getPackages().forEach(pkgName -> { + if (isJDKModulePackage(module.getName())) { + addOpensToAll(module, pkgName); + addExportsToAll(module, pkgName); + } + })); + } + } catch (Throwable t) { + LOGGER.error("Failed to export all JDK module package to all", t); + } + } + + private static boolean isJDKModulePackage(String modulePackageName) { + return modulePackageName.startsWith("java.") || modulePackageName.startsWith("jdk."); + } + + /** + * Export all module packages to all. + */ + public static void exportAllModulePackageToAll() { + try { + if (isExported.compareAndSet(false,true) && nameToModules != null) { + nameToModules.forEach((name, module) -> module.getPackages().forEach(pkgName -> { + addOpensToAll(module, pkgName); + addExportsToAll(module, pkgName); + })); + } + } catch (Throwable t) { + LOGGER.error("Failed to export all module package to all", t); + } + } + + /** + * Updates this module to open a package to all unnamed modules. + * + * @param moduleName + * @param packageName + */ + public static boolean addOpensToAllUnnamed(String moduleName, String packageName) { + return invokeModuleMethod(implAddOpensToAllUnnamed, moduleName, packageName); + } + + /** + * Updates this module to open a package to all unnamed modules. + * + * @param module + * @param packageName + */ + public static boolean addOpensToAllUnnamed(Module module, String packageName) { + return invokeModuleMethod(implAddOpensToAllUnnamed, module, packageName); + } + + /** + * Updates this module to export a package to all unnamed modules. + * + * @param moduleName + * @param packageName + */ + public static boolean addExportsToAllUnnamed(String moduleName, String packageName) { + return invokeModuleMethod(implAddExportsToAllUnnamed, moduleName, packageName); + } + + /** + * Updates this module to export a package to all unnamed modules. + * + * @param module + * @param packageName + */ + public static boolean addExportsToAllUnnamed(Module module, String packageName) { + return invokeModuleMethod(implAddExportsToAllUnnamed, module, packageName); + } + + /** + * Updates this module to open a package to another module. + * + * @param moduleName + * @param packageName + */ + public static boolean addOpensToAll(String moduleName, String packageName) { + + return invokeModuleMethod(implAddOpens, moduleName, packageName); + } + + /** + * Updates this module to open a package to another module. + * + * @param module + * @param packageName + */ + public static boolean addOpensToAll(Module module, String packageName) { + + return invokeModuleMethod(implAddOpens, module, packageName); + } + + /** + * Updates this module to export a package unconditionally. + * @param moduleName + * @param packageName + */ + public static boolean addExportsToAll(String moduleName, String packageName) { + return invokeModuleMethod(implAddExports, moduleName, packageName); + } + + /** + * Updates this module to export a package unconditionally. + * @param module + * @param packageName + */ + public static boolean addExportsToAll(Module module, String packageName) { + return invokeModuleMethod(implAddExports, module, packageName); + } + + /** + * invoke ModuleLayer method + * + * @param method + * @param moduleName + * @param packageName + * @return + */ + public static boolean invokeModuleMethod(MethodHandle method, String moduleName, + String packageName) { + Optional findModule = ModuleLayer.boot().findModule(moduleName); + if (findModule.isPresent()) { + try { + return invokeModuleMethod(method, findModule.get(), packageName); + } catch (Throwable t) { + LOGGER.error("Failed to invoke ModuleLayer method: {}", method, t); + } + } + return false; + } + + public static boolean invokeModuleMethod(MethodHandle method, Module module, String packageName) { + try { + method.invoke(module, packageName); + return true; + } catch (Throwable t) { + LOGGER.error("Failed to invoke Module method: {}", method, t); + } + return false; + } + + /** + * create MethodHandle from Module + * + * @param methodName + * @param parameterTypes + * @return MethodHandle + */ + private static MethodHandle createModuleMethodHandle(String methodName, + Class... parameterTypes) { + try { + return UnsafeUtil.implLookup().unreflect( + Module.class.getDeclaredMethod(methodName, parameterTypes)); + } catch (Throwable t) { + LOGGER.error("Failed to create Module method handle: {}", methodName, t); + } + return null; + } + + /** + * Get ModuleLayer.bootLayer field value + * + * @param fieldName + * @return field value + */ + private static Object getModuleLayerFieldsValue(String fieldName) { + ModuleLayer moduleLayer = ModuleLayer.boot(); + try { + Class moduleLayerClass = ModuleLayer.class; + Field field = moduleLayerClass.getDeclaredField(fieldName); + return UnsafeUtil.implLookup().unreflectVarHandle(field).get(moduleLayer); + } catch (Throwable t) { + LOGGER.error("Failed to get ModuleLayer field value: {}", fieldName, t); + } + return null; + } + + /** + * Get all modules from System.bootLayer + * + * @return modules + */ + @SuppressWarnings("unchecked") + public static Map getNameToModule() { + return (Map) getModuleLayerFieldsValue("nameToModule"); + } +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java new file mode 100644 index 000000000..0bbbca199 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.util; + +import sun.misc.Unsafe; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; + +/** + * @author huazhongming + * @since 4.4.0 + */ +public class UnsafeUtil { + private static Unsafe UNSAFE; + private static MethodHandles.Lookup IMPL_LOOKUP; + + public static Unsafe unsafe() { + if (UNSAFE == null) { + Unsafe unsafe = null; + try { + Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeField.setAccessible(true); + unsafe = (Unsafe) theUnsafeField.get(null); + } catch (Throwable ignored) { + } + UNSAFE = unsafe; + } + + return UNSAFE; + } + + public static MethodHandles.Lookup implLookup() { + if (IMPL_LOOKUP == null) { + Class lookupClass = MethodHandles.Lookup.class; + + try { + Field implLookupField = lookupClass.getDeclaredField("IMPL_LOOKUP"); + long offset = unsafe().staticFieldOffset(implLookupField); + IMPL_LOOKUP = (MethodHandles.Lookup) unsafe().getObject( + unsafe().staticFieldBase(implLookupField), offset); + } catch (Throwable ignored) { + } + } + return IMPL_LOOKUP; + } +} diff --git a/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories b/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories index 1aea1f8c4..9ed35930a 100644 --- a/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories +++ b/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories @@ -15,4 +15,5 @@ org.springframework.boot.SpringApplicationRunListener=\ # Initializers org.springframework.context.ApplicationContextInitializer=\ - com.alipay.sofa.boot.compatibility.CompatibilityVerifierApplicationContextInitializer + com.alipay.sofa.boot.compatibility.CompatibilityVerifierApplicationContextInitializer,\ + com.alipay.sofa.boot.Initializer.AutoModuleExportApplicationContextInitializer diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreatorTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreatorTests.java new file mode 100644 index 000000000..ba9fd9fd9 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreatorTests.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.aop.framework.autoproxy; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ExcludeBeanNameAutoProxyCreator}. + * + * @author huzijie + * @version ExcludeBeanNameAutoProxyCreatorTests.java, v 0.1 2024年01月04日 4:36 PM huzijie Exp $ + */ +public class ExcludeBeanNameAutoProxyCreatorTests { + + @Test + public void excludeBeanNames() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + ExcludeBeanNameAutoProxyCreatorTestConfiguration.class); + SampleInterface sampleA = context.getBean("sampleA", SampleInterface.class); + SampleInterface sampleB = context.getBean("sampleBeanB", SampleInterface.class); + SampleInterface sampleC = context.getBean("sampleBeanC", SampleInterface.class); + assertThat(sampleA.hello()).isEqualTo("hello"); + assertThat(sampleB.hello()).isEqualTo("aop"); + assertThat(sampleC.hello()).isEqualTo("hello"); + } + + @Configuration + static class ExcludeBeanNameAutoProxyCreatorTestConfiguration { + + @Bean + public SampleInterface sampleA() { + return new SampleInterfaceImpl(); + } + + @Bean + public SampleInterface sampleBeanB() { + return new SampleInterfaceImpl(); + } + + @Bean + public SampleInterface sampleBeanC() { + return new SampleInterfaceImpl(); + } + + @Bean + public ExcludeBeanNameAutoProxyCreator excludeBeanNameAutoProxyCreator() { + ExcludeBeanNameAutoProxyCreator autoProxyCreator = new ExcludeBeanNameAutoProxyCreator(); + autoProxyCreator.setBeanNames("sampleBean*"); + autoProxyCreator.setExcludeBeanNames("sampleBeanC"); + autoProxyCreator.setInterceptorNames("sampleAdvisor"); + return autoProxyCreator; + } + + @Bean + public MethodInterceptor sampleAdvisor() { + return new MethodInterceptor() { + @Nullable + @Override + public Object invoke(@Nonnull MethodInvocation invocation) { + return "aop"; + } + }; + } + } + + interface SampleInterface { + + String hello(); + } + + static class SampleInterfaceImpl implements SampleInterface { + + @Override + public String hello() { + return "hello"; + } + } +} diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/compatibility/VerificationResultTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/compatibility/VerificationResultTests.java new file mode 100644 index 000000000..664ab4a5c --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/compatibility/VerificationResultTests.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.compatibility; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link VerificationResult}. + * + * @author JPSINH27 + * @version VerificationResultTests, v 0.1 2024年03月02日 10:20 PM + */ +public class VerificationResultTests { + + @Test + public void testEquals_SameDescriptionAndAction_ReturnsTrue() { + VerificationResult result1 = VerificationResult.notCompatible("Error", "Take action"); + VerificationResult result2 = VerificationResult.notCompatible("Error", "Take action"); + assertThat(result1).isEqualTo(result2); + } + + @Test + public void testEquals_DifferentDescriptions_ReturnsFalse() { + VerificationResult result1 = VerificationResult.notCompatible("Error 1", "Take action"); + VerificationResult result2 = VerificationResult.notCompatible("Error 2", "Take action"); + assertThat(result1).isNotEqualTo(result2); + } + + @Test + public void testEquals_DifferentActions_ReturnsFalse() { + VerificationResult result1 = VerificationResult.notCompatible("Error", "Take action 1"); + VerificationResult result2 = VerificationResult.notCompatible("Error", "Take action 2"); + assertThat(result1).isNotEqualTo(result2); + } + + @Test + public void testEquals_ComparingWithNull_ReturnsFalse() { + VerificationResult result1 = VerificationResult.notCompatible("Error", "Take action"); + assertThat(result1).isNotEqualTo(null); + } +} \ No newline at end of file diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/initializer/AutoModuleExportApplicationContextInitializerTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/initializer/AutoModuleExportApplicationContextInitializerTests.java new file mode 100644 index 000000000..e9d30fa48 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/initializer/AutoModuleExportApplicationContextInitializerTests.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.initializer; + +import com.alipay.sofa.boot.Initializer.AutoModuleExportApplicationContextInitializer; +import com.alipay.sofa.boot.util.ModuleUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; + +/** + * @author huazhongming + * @since 4.4.0 + */ +public class AutoModuleExportApplicationContextInitializerTests { + + private ApplicationContextRunner contextRunner; + + @BeforeEach + void setUp() { + contextRunner = new ApplicationContextRunner() + .withInitializer(new AutoModuleExportApplicationContextInitializer()); + } + + @Test + void jdkDefaultTrue(){ + + try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) { + contextRunner.withPropertyValues().run(applicationContext -> {}); + mockedStatic.verify(ModuleUtil::exportAllJDKModulePackageToAll, times(1)); + } + } + + @Test + void allDefaultFalse(){ + try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) { + contextRunner.withPropertyValues().run(applicationContext -> {}); + mockedStatic.verify(ModuleUtil::exportAllModulePackageToAll, times(0)); + } + } + + @Test + void jdkDisable(){ + + try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) { + contextRunner.withPropertyValues("sofa.boot.auto.module.export.jdk.enable=false").run(applicationContext -> {}); + mockedStatic.verify(ModuleUtil::exportAllJDKModulePackageToAll, times(0)); + } + } + + @Test + void allEnable(){ + try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) { + contextRunner.withPropertyValues("sofa.boot.auto.module.export.all.enable=true").run(applicationContext -> {}); + mockedStatic.verify(ModuleUtil::exportAllModulePackageToAll, times(1)); + } + } +} diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/startup/StartupReporterTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/startup/StartupReporterTests.java new file mode 100644 index 000000000..8a405b1d5 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/startup/StartupReporterTests.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 com.alipay.sofa.boot.startup; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Tests for {@link StartupReporter}. + * + * @author JPSINH27 + * @version StartupReporterTests.java, v 0.1 2024年01月03日 10:19 PM + */ +public class StartupReporterTests { + + @Mock + ConfigurableApplicationContext mockContext; + + @Mock + ConfigurableEnvironment mockEnvironment; + + @BeforeEach + public void setup() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void testApplicationBootFinish() { + StartupReporter startupReporter = new StartupReporter(); + assertDoesNotThrow(startupReporter::applicationBootFinish); + } + + @Test + public void testAddCommonStartupStat() { + StartupReporter startupReporter = new StartupReporter(); + BaseStat baseStat = new BaseStat(); + assertDoesNotThrow(() -> { + startupReporter.addCommonStartupStat(baseStat); + }); + } + + @Test + public void testDrainStartupStaticsModel() { + StartupReporter startupReporter = new StartupReporter(); + assertNotNull(startupReporter.drainStartupStaticsModel()); + } + +} \ No newline at end of file diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/ModuleUtilTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/ModuleUtilTests.java new file mode 100644 index 000000000..d574a05ff --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/ModuleUtilTests.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 com.alipay.sofa.boot.util; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.InaccessibleObjectException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author huazhongming + * @since 4.4.0 + */ +public class ModuleUtilTests { + + @Test + public void testExportAllJDKModulePackageToAll() throws NoSuchMethodException { + + Exception exception = assertThrows(InaccessibleObjectException.class,() -> { + Method newByteChannel0Method = Files.class.getDeclaredMethod("provider", Path.class); + newByteChannel0Method.setAccessible(true); + }); + + assertTrue(exception.getMessage().contains("module java.base does not \"opens java.nio.file\" to unnamed module")); + + ModuleUtil.exportAllJDKModulePackageToAll(); + + Method newByteChannel0Method = Files.class.getDeclaredMethod("provider", Path.class); + newByteChannel0Method.setAccessible(true); + } +} diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/SofaBootEnvUtilsTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/SofaBootEnvUtilsTests.java index 8e8d8aad1..29b8e84e3 100644 --- a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/SofaBootEnvUtilsTests.java +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/SofaBootEnvUtilsTests.java @@ -19,6 +19,8 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * Tests for {@link SofaBootEnvUtils}. @@ -49,4 +51,38 @@ public void testEnv() { public void arkEnv() { assertThat(SofaBootEnvUtils.isArkEnv()).isFalse(); } + + @Test + void testIsSpringCloudBootstrapEnvironment_NullEnvironment() { + assertFalse(SofaBootEnvUtils.isSpringCloudBootstrapEnvironment(null)); + } + + @Test + public void testInitSpringTestEnv() { + + boolean expectedTestEnv = true; + + boolean actualTestEnv = isInitSpringTestEnv(); + + assertEquals(expectedTestEnv, actualTestEnv); + } + + private boolean isInitSpringTestEnv() { + StackTraceElement[] stackTrace = new StackTraceElement[] { + new StackTraceElement("SomeClass", "someMethod", "SomeClass.java", 10), + new StackTraceElement("AnotherClass", "loadContext", "AnotherClass.java", 20), + new StackTraceElement( + "org.springframework.boot.test.context.SpringBootContextLoader", "loadContext", + "SpringBootContextLoader.java", 30) }; + boolean TEST_ENV = false; + for (StackTraceElement stackTraceElement : stackTrace) { + if ("loadContext".equals(stackTraceElement.getMethodName()) + && "org.springframework.boot.test.context.SpringBootContextLoader" + .equals(stackTraceElement.getClassName())) { + TEST_ENV = true; + break; + } + } + return TEST_ENV; + } } diff --git a/sofa-boot-project/sofaboot-dependencies/pom.xml b/sofa-boot-project/sofaboot-dependencies/pom.xml index 7f335ef48..7bac50f75 100644 --- a/sofa-boot-project/sofaboot-dependencies/pom.xml +++ b/sofa-boot-project/sofaboot-dependencies/pom.xml @@ -10,6 +10,7 @@ ${revision} + SOFABoot Dependencies sofaboot-dependencies pom @@ -19,20 +20,20 @@ 0.4 3.19.0 3.1.2 - 0.8.9 + 0.8.13 3.0.0 6.1.8 - 4.0.0 - 5.11.0 - 2.0.1 - 1.6.6 - 3.5.1 - 2.2.3 + 4.0.2 + 5.13.4 + 2.1.1 + 1.6.10 + 3.5.5 + 2.2.7 1.6.1 - 2022.0.3 - 9.4 + 2025.0.0 + 9.8 1.2.83 3.29.2-GA 3.22.2 @@ -537,18 +538,6 @@ ${commons.io.version} - - mysql - mysql-connector-java - ${mysql.version} - - - com.google.protobuf - protobuf-java - - - - com.alibaba druid diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleHealthChecker.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleHealthChecker.java new file mode 100644 index 000000000..a058d208d --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleHealthChecker.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 com.alipay.sofa.smoke.tests.actuator.sample.readiness; + +import com.alipay.sofa.boot.actuator.health.HealthChecker; +import org.springframework.boot.actuate.health.Health; + +/** + * @author huzijie + * @version SampleHealthChecker.java, v 0.1 2023年11月24日 4:08 PM huzijie Exp $ + */ +public class SampleHealthChecker implements HealthChecker { + + private final long sleep; + + public SampleHealthChecker(long sleep) { + this.sleep = sleep; + } + + @Override + public Health isHealthy() { + if (sleep > 0) { + try { + Thread.sleep(sleep); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + return Health.up().build(); + } + + @Override + public String getComponentName() { + return "sample"; + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleHealthIndicate.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleHealthIndicate.java new file mode 100644 index 000000000..7d502ab33 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleHealthIndicate.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.actuator.sample.readiness; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; + +/** + * @author huzijie + * @version SampleHealthIndicate.java, v 0.1 2023年11月24日 4:08 PM huzijie Exp $ + */ +public class SampleHealthIndicate implements HealthIndicator { + + private final long sleep; + + public SampleHealthIndicate(long sleep) { + this.sleep = sleep; + } + + @Override + public Health health() { + if (sleep > 0) { + try { + Thread.sleep(sleep); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + return Health.up().build(); + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleReadinessCheckCallback.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleReadinessCheckCallback.java new file mode 100644 index 000000000..0786f769b --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/main/java/com/alipay/sofa/smoke/tests/actuator/sample/readiness/SampleReadinessCheckCallback.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.actuator.sample.readiness; + +import com.alipay.sofa.boot.actuator.health.ReadinessCheckCallback; +import org.springframework.boot.actuate.health.Health; +import org.springframework.context.ApplicationContext; + +/** + * @author huzijie + * @version SampleReadinessCheckCallback.java, v 0.1 2023年11月24日 4:09 PM huzijie Exp $ + */ +public class SampleReadinessCheckCallback implements ReadinessCheckCallback { + private final long sleep; + + public SampleReadinessCheckCallback(long sleep) { + this.sleep = sleep; + } + + @Override + public Health onHealthy(ApplicationContext applicationContext) { + return Health.up().build(); + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/ParallelReadinessHealthCheckTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/ParallelReadinessHealthCheckTests.java new file mode 100644 index 000000000..6e4d9b0b0 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/ParallelReadinessHealthCheckTests.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.actuator.health; + +import org.springframework.test.context.TestPropertySource; + +/** + * Integration tests for multi components in parallel mode. + * + * @author huzijie + * @version ParallelReadinessHealthCheckTests.java, v 0.1 2023年11月27日 11:30 AM huzijie Exp $ + */ +@TestPropertySource(properties = { "sofa.boot.actuator.health.parallelCheck=true", "bean.count=100" }) +public class ParallelReadinessHealthCheckTests extends ReadinessHealthCheckTestBase { +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/ReadinessHealthCheckTestBase.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/ReadinessHealthCheckTestBase.java new file mode 100644 index 000000000..a95f83f84 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/ReadinessHealthCheckTestBase.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.actuator.health; + +import com.alipay.sofa.boot.actuator.health.ReadinessCheckListener; +import com.alipay.sofa.boot.context.processor.UnshareSofaPostProcessor; +import com.alipay.sofa.smoke.tests.actuator.ActuatorSofaBootApplication; +import com.alipay.sofa.smoke.tests.actuator.sample.readiness.SampleHealthChecker; +import com.alipay.sofa.smoke.tests.actuator.sample.readiness.SampleHealthIndicate; +import com.alipay.sofa.smoke.tests.actuator.sample.readiness.SampleReadinessCheckCallback; +import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.boot.availability.ReadinessState; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Random; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Base integration tests for multi components. + * + * @author huzijie + * @version ReadinessHealthCheckTestBase.java, v 0.1 2023年11月24日 4:06 PM huzijie Exp $ + */ +@SpringBootTest(classes = ActuatorSofaBootApplication.class) +@Import(ReadinessHealthCheckTestBase.ReadinessBeanDefinitionRegistryPostProcessor.class) +public class ReadinessHealthCheckTestBase { + + @Value("${bean.count}") + private int beanCount; + + @Autowired + private ReadinessCheckListener readinessCheckListener; + + @Test + public void checkReadinessResult() { + assertThat(readinessCheckListener.getReadinessState()).isEqualTo( + ReadinessState.ACCEPTING_TRAFFIC); + assertThat(readinessCheckListener.getHealthCheckerDetails().size()).isEqualTo(beanCount); + assertThat(readinessCheckListener.getHealthIndicatorDetails().size()).isEqualTo(beanCount); + assertThat(readinessCheckListener.getHealthCallbackDetails().size()).isEqualTo(beanCount); + } + + @UnshareSofaPostProcessor + static class ReadinessBeanDefinitionRegistryPostProcessor implements + BeanDefinitionRegistryPostProcessor, + EnvironmentAware { + + private int beanCount; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) + throws BeansException { + if (registry.containsBeanDefinition("sofaModuleHealthChecker")) { + registry.removeBeanDefinition("sofaModuleHealthChecker"); + } + if (registry.containsBeanDefinition("sofaComponentHealthChecker")) { + registry.removeBeanDefinition("sofaComponentHealthChecker"); + } + if (registry.containsBeanDefinition("diskSpaceHealthIndicator")) { + registry.removeBeanDefinition("diskSpaceHealthIndicator"); + } + if (registry.containsBeanDefinition("pingHealthContributor")) { + registry.removeBeanDefinition("pingHealthContributor"); + } + if (registry.containsBeanDefinition("rpcAfterHealthCheckCallback")) { + registry.removeBeanDefinition("rpcAfterHealthCheckCallback"); + } + + registerRandomBeanDefinitions(registry, SampleHealthChecker.class); + registerRandomBeanDefinitions(registry, SampleHealthIndicate.class); + registerRandomBeanDefinitions(registry, SampleReadinessCheckCallback.class); + } + + private void registerRandomBeanDefinitions(BeanDefinitionRegistry registry, Class clazz) { + Set numbers = new HashSet<>(); + Random random = new Random(); + + while (numbers.size() < beanCount) { + int number = random.nextInt(1000); + numbers.add(number); + } + + numbers.forEach(sleep -> register(registry, clazz, sleep)); + } + + private void register(BeanDefinitionRegistry registry, Class clazz, long sleep) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(clazz); + ConstructorArgumentValues values = new ConstructorArgumentValues(); + values.addIndexedArgumentValue(0, sleep); + beanDefinition.setConstructorArgumentValues(values); + registry.registerBeanDefinition(clazz.getSimpleName() + sleep, beanDefinition); + } + + @Override + public void setEnvironment(Environment environment) { + this.beanCount = Integer.parseInt(Objects.requireNonNull(environment + .getProperty("bean.count"))); + } + } + +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/SerialReadinessHealthCheckTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/SerialReadinessHealthCheckTests.java new file mode 100644 index 000000000..3c7025f44 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/SerialReadinessHealthCheckTests.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.actuator.health; + +import org.springframework.test.context.TestPropertySource; + +/** + * Integration tests for multi components in serial mode. + * + * @author huzijie + * @version SerialReadinessHealthCheckTests.java, v 0.1 2023年11月27日 11:29 AM huzijie Exp $ + */ +@TestPropertySource(properties = { "sofa.boot.actuator.health.parallelCheck=false", "bean.count=5" }) +public class SerialReadinessHealthCheckTests extends ReadinessHealthCheckTestBase { +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/VirtualParallelReadinessHealthCheckTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/VirtualParallelReadinessHealthCheckTests.java new file mode 100644 index 000000000..3be63a77c --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/health/VirtualParallelReadinessHealthCheckTests.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.actuator.health; + +import org.springframework.test.context.TestPropertySource; + +/** + * Integration tests for multi components in parallel mode when use virtual threads. + * + * @author huzijie + * @version VirtualParallelReadinessHealthCheckTests.java, v 0.1 2023年11月27日 11:31 AM huzijie Exp $ + */ +@TestPropertySource(properties = { "sofa.boot.startup.threads.virtual.enabled=true", + "bean.count=300" }) +public class VirtualParallelReadinessHealthCheckTests extends ParallelReadinessHealthCheckTests { +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/threadpool/ThreadPoolEndpointWebTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/threadpool/ThreadPoolEndpointWebTests.java new file mode 100644 index 000000000..c516efd56 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-actuator/src/test/java/com/alipay/sofa/smoke/tests/actuator/threadpool/ThreadPoolEndpointWebTests.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.actuator.threadpool; + +import com.alipay.sofa.common.thread.SofaThreadPoolExecutor; +import com.alipay.sofa.smoke.tests.actuator.ActuatorSofaBootApplication; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link com.alipay.sofa.boot.actuator.threadpool.ThreadPoolEndpoint} web response. + * + * @author huzijie + * @version ThreadPoolEndpointWebTests.java, v 0.1 2024年03月22日 14:06 huzijie Exp $ + */ +@SpringBootTest(classes = ActuatorSofaBootApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "management.endpoints.web.exposure.include=threadpool" }) +@Import(ThreadPoolEndpointWebTests.Config.class) +public class ThreadPoolEndpointWebTests { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void threadPoolActuator() { + ResponseEntity response = restTemplate.getForEntity("/actuator/threadpool", String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()) + .contains(""" + {"threadPoolName":"demoThreadPool","threadPoolClassName":"com.alipay.sofa.common.thread.SofaThreadPoolExecutor","coreSize":20,"maxSize":20,"queueClassName":"java.util.concurrent.LinkedBlockingQueue","queueSize":0,"queueRemainingCapacity":10,"monitorPeriod":5000,"taskTimeout":30000}"""); + } + + @Configuration + static class Config { + + @Bean + public SofaThreadPoolExecutor sofaThreadPoolExecutor() { + return new SofaThreadPoolExecutor(20, 20, 30, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(10), "demoThreadPool"); + } + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-boot/src/test/java/com/alipay/sofa/smoke/tests/boot/StartupApplicationStartupTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-boot/src/test/java/com/alipay/sofa/smoke/tests/boot/StartupApplicationStartupTests.java new file mode 100644 index 000000000..a4bc524cc --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-boot/src/test/java/com/alipay/sofa/smoke/tests/boot/StartupApplicationStartupTests.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 com.alipay.sofa.smoke.tests.boot; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.metrics.ApplicationStartup; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author huzijie + * @version StartupApplicationStartupTests.java, v 0.1 2024年05月23日 17:40 huzijie Exp $ + */ +@SpringBootTest +public class StartupApplicationStartupTests { + + @Autowired + private ConfigurableApplicationContext context; + + @Test + public void checkBufferApplicationStartup() { + ApplicationStartup applicationStartup = context.getApplicationStartup(); + assertThat(applicationStartup).isInstanceOf(BufferingApplicationStartup.class); + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-isle/src/test/java/com/alipay/sofa/smoke/tests/isle/ParallelSpringContextInstallStageTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-isle/src/test/java/com/alipay/sofa/smoke/tests/isle/ParallelSpringContextInstallStageTests.java index ef1ece14e..bd1d92431 100644 --- a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-isle/src/test/java/com/alipay/sofa/smoke/tests/isle/ParallelSpringContextInstallStageTests.java +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-isle/src/test/java/com/alipay/sofa/smoke/tests/isle/ParallelSpringContextInstallStageTests.java @@ -17,6 +17,8 @@ package com.alipay.sofa.smoke.tests.isle; import com.alipay.sofa.boot.isle.stage.SpringContextInstallStage; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.springframework.test.context.TestPropertySource; /** @@ -26,6 +28,7 @@ * @version ParallelSpringContextInstallStageTests.java, v 0.1 2023年02月21日 8:06 PM huzijie Exp $ */ @TestPropertySource(properties = "sofa.boot.isle.moduleStartUpParallel=true") +@EnabledOnJre(JRE.JAVA_17) public class ParallelSpringContextInstallStageTests extends SpringContextInstallStageIntegrationTestBase { } diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-isle/src/test/java/com/alipay/sofa/smoke/tests/isle/ParallelVirtualThreadSpringContextInstallStageTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-isle/src/test/java/com/alipay/sofa/smoke/tests/isle/ParallelVirtualThreadSpringContextInstallStageTests.java new file mode 100644 index 000000000..88e116de0 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-isle/src/test/java/com/alipay/sofa/smoke/tests/isle/ParallelVirtualThreadSpringContextInstallStageTests.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.isle; + +import com.alipay.sofa.boot.isle.stage.SpringContextInstallStage; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.springframework.test.context.TestPropertySource; + +/** + * Integration tests for {@link SpringContextInstallStage} in parallel when use virtual threads. + * + * @author huzijie + * @version ParallelVirtualThreadSpringContextInstallStageTests.java, v 0.1 2023年11月24日 4:03 PM huzijie Exp $ + */ +@TestPropertySource(properties = { "sofa.boot.startup.threads.virtual.enabled=true" }) +@EnabledOnJre(JRE.JAVA_21) +public class ParallelVirtualThreadSpringContextInstallStageTests extends + ParallelSpringContextInstallStageTests { +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/pom.xml b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/pom.xml index e96912d29..0bb54214d 100644 --- a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/pom.xml +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/pom.xml @@ -32,6 +32,7 @@ ${basedir}/../../.. 0.0.3 + 3.17.3 @@ -115,7 +116,7 @@ protobuf-maven-plugin 0.5.1 - com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} + com.google.protobuf:protoc:${protobuf.protoc.version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} build/generated/source/proto/test/java diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java index 4092a5c79..66ea0eb85 100644 --- a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/RpcSofaBootApplication.java @@ -18,16 +18,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.FilterType; /** * * @author yuanxuan * @version : RpcSofaBootApplication.java, v 0.1 2023年02月03日 15:19 yuanxuan Exp $ */ -@SpringBootApplication -@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.alipay.sofa.smoke.tests.rpc.boot.*") }) +@SpringBootApplication(scanBasePackages = "none") public class RpcSofaBootApplication { public static void main(String[] args) { diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/bean/delayregister/DelayRegisterService.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/bean/delayregister/DelayRegisterService.java new file mode 100644 index 000000000..8f2f3f713 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/bean/delayregister/DelayRegisterService.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.rpc.boot.bean.delayregister; + +/** + * @author chengming + * @version DelayRegisterService.java, v 0.1 2024年02月26日 3:37 PM chengming + */ +public interface DelayRegisterService { + + String sayHello(String string); +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/bean/delayregister/DelayRegisterServiceImpl.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/bean/delayregister/DelayRegisterServiceImpl.java new file mode 100644 index 000000000..39bdd1bfd --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/java/com/alipay/sofa/smoke/tests/rpc/boot/bean/delayregister/DelayRegisterServiceImpl.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.rpc.boot.bean.delayregister; + +/** + * @author chengming + * @version DelayRegisterServiceImpl.java, v 0.1 2024年02月26日 3:39 PM chengming + */ +public class DelayRegisterServiceImpl implements DelayRegisterService { + + @Override + public String sayHello(String string) { + return string; + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/resources/spring/test_only_delay_register.xml b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/resources/spring/test_only_delay_register.xml new file mode 100644 index 000000000..8f16189f6 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/main/resources/spring/test_only_delay_register.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterFailTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterFailTests.java new file mode 100644 index 000000000..dfb2aac0e --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterFailTests.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 com.alipay.sofa.smoke.tests.rpc.delayregister; + +import org.junit.jupiter.api.Test; +import org.springframework.test.context.TestPropertySource; + +/** + * Tests for rpc provider delay register when health check failed. + * + * @author huzijie + * @version DelayRegisterFailTests.java, v 0.1 2024年02月26日 20:15 huzijie Exp $ + */ +@TestPropertySource(properties = { "delayregister.healthcheck.result=false", "test-uniqueId=fail" }) +public class DelayRegisterFailTests extends DelayRegisterTestsBase { + + @Test + public void testDelayRegister() throws InterruptedException { + registerFail(); + + Thread.sleep(3000); + + registerFail(); + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterSuccessTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterSuccessTests.java new file mode 100644 index 000000000..f46b192b9 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterSuccessTests.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 com.alipay.sofa.smoke.tests.rpc.delayregister; + +import org.junit.jupiter.api.Test; +import org.springframework.test.context.TestPropertySource; + +/** + * Tests for rpc provider delay register when health check success. + * + * @author huzijie + * @version DelayRegisterSuccessTests.java, v 0.1 2024年02月26日 20:15 huzijie Exp $ + */ +@TestPropertySource(properties = { "delayregister.healthcheck.result=true", "test-uniqueId=success" }) +public class DelayRegisterSuccessTests extends DelayRegisterTestsBase { + + @Test + public void testDelayRegister() throws InterruptedException { + registerFail(); + + Thread.sleep(3000); + + registerSuccess(); + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterTestsBase.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterTestsBase.java new file mode 100644 index 000000000..63201c81b --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-rpc/src/test/java/com/alipay/sofa/smoke/tests/rpc/delayregister/DelayRegisterTestsBase.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 com.alipay.sofa.smoke.tests.rpc.delayregister; + +import com.alipay.sofa.boot.actuator.health.ReadinessCheckCallback; +import com.alipay.sofa.rpc.core.exception.SofaRouteException; +import com.alipay.sofa.smoke.tests.rpc.boot.RpcSofaBootApplication; +import com.alipay.sofa.smoke.tests.rpc.boot.bean.delayregister.DelayRegisterService; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportResource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Base tests for rpc provider delay register. + * + * @author chengming + * @version DelayRegisterTests.java, v 0.1 2024年02月26日 3:58 PM chengming + */ +@SpringBootTest(classes = RpcSofaBootApplication.class, properties = { + "sofa.boot.rpc.registry.address=zookeeper://127.0.0.1:2181", + "sofa.boot.rpc.enable-auto-publish=true", + "sofa.boot.rpc.enable-delay-register=true" }) +@Import(DelayRegisterTestsBase.DelayRegisterConfiguration.class) +public class DelayRegisterTestsBase { + + @BeforeAll + public static void setUp() { + System.setProperty("provider.delay", "2000"); + } + + @AfterAll + public static void clear() { + System.clearProperty("provider.delay"); + } + + @Autowired + private DelayRegisterService delayRegisterService; + + protected void registerSuccess() { + String hi = delayRegisterService.sayHello("hi"); + assertThat(hi).isEqualTo("hi"); + } + + protected void registerFail() { + assertThatThrownBy(() -> delayRegisterService.sayHello("hi")).isInstanceOf(SofaRouteException.class). + hasMessageContaining("Cannot get the service address of service"); + } + + @Configuration + @ImportResource("/spring/test_only_delay_register.xml") + static class DelayRegisterConfiguration { + + } + + public static class CustomReadinessCallBack implements ReadinessCheckCallback { + + @Value("${delayregister.healthcheck.result}") + private boolean result; + + @Override + public Health onHealthy(ApplicationContext applicationContext) { + return result ? Health.up().build() : Health.down().build(); + } + } +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/async/AsyncInitTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/async/AsyncInitTests.java index 1f2834023..13b51ac55 100644 --- a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/async/AsyncInitTests.java +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/async/AsyncInitTests.java @@ -20,6 +20,8 @@ import com.alipay.sofa.runtime.async.AsyncInitMethodManager; import com.alipay.sofa.smoke.tests.runtime.RuntimeSofaBootApplication; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; @@ -41,6 +43,7 @@ @Import(AsyncInitTests.AsyncInitTestConfiguration.class) @TestPropertySource(properties = { "sofa.boot.runtime.asyncInitExecutorCoreSize=20", "sofa.boot.runtime.asyncInitExecutorMaxSize=20" }) +@EnabledOnJre(JRE.JAVA_17) public class AsyncInitTests { @Autowired diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/async/AsyncInitVirtualThreadTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/async/AsyncInitVirtualThreadTests.java new file mode 100644 index 000000000..74c4c3047 --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/async/AsyncInitVirtualThreadTests.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.smoke.tests.runtime.async; + +import com.alipay.sofa.runtime.async.AsyncInitMethodManager; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.springframework.test.context.TestPropertySource; + +/** + * Integration tests for {@link AsyncInitMethodManager} when use virtual threads. + * + * @author huzijie + * @version AsyncInitVirtualThreadTests.java, v 0.1 2023年11月24日 4:00 PM huzijie Exp $ + */ +@TestPropertySource(properties = { "sofa.boot.startup.threads.virtual.enabled=true" }) +@EnabledOnJre(JRE.JAVA_21) +public class AsyncInitVirtualThreadTests extends AsyncInitTests { +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/spring/SofaBeanLazyInitTests.java b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/spring/SofaBeanLazyInitTests.java new file mode 100644 index 000000000..5b46140ef --- /dev/null +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-runtime/src/test/java/com/alipay/sofa/smoke/tests/runtime/spring/SofaBeanLazyInitTests.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 com.alipay.sofa.smoke.tests.runtime.spring; + +import com.alipay.sofa.runtime.api.annotation.SofaReference; +import com.alipay.sofa.runtime.api.annotation.SofaService; +import com.alipay.sofa.runtime.service.component.impl.ServiceImpl; +import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; +import com.alipay.sofa.smoke.tests.runtime.RuntimeSofaBootApplication; +import com.alipay.sofa.smoke.tests.runtime.service.SampleService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.stereotype.Component; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link SofaService} and {@link SofaReference} Bean Lazy Initialization. + * + * @author Jermaine Hua + */ +@SpringBootTest(classes = RuntimeSofaBootApplication.class, properties = "spring.main.lazy-initialization=true") +@Import(SofaBeanLazyInitTests.ServiceBeanAnnotationConfiguration.class) +public class SofaBeanLazyInitTests { + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkSofaServiceBeanDefinitionLazyInitAttribute() { + String beanName = SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, + "sampleServiceImpl"); + assertThat(applicationContext.containsBean(beanName)).isTrue(); + assertThat(applicationContext.getBean(beanName)).isInstanceOf(ServiceImpl.class); + + assertThat( + ((GenericApplicationContext) applicationContext).getBeanFactory() + .getBeanDefinition(beanName).isLazyInit()).isTrue(); + + } + + @Test + public void checkSofaReferenceBeanDefinitionLazyInitAttribute() { + String beanName = SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class, + ""); + assertThat(applicationContext.containsBean(beanName)).isTrue(); + assertThat( + ((GenericApplicationContext) applicationContext).getBeanFactory() + .getBeanDefinition(beanName).isLazyInit()).isTrue(); + + } + + @Configuration + @Import({ SampleServiceImpl.class }) + static class ServiceBeanAnnotationConfiguration { + @Bean + public SampleService methodSampleService(@SofaReference SampleService sampleServiceImpl) { + return () -> "methodSampleService"; + } + } + + @SofaService + @Component("sampleServiceImpl") + static class SampleServiceImpl implements SampleService { + + @Override + public String service() { + return "sampleService"; + } + } + +} diff --git a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml index 5c33fd88e..13126d1c1 100644 --- a/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml +++ b/sofa-boot-tests/sofa-boot-smoke-tests/sofa-boot-smoke-tests-tracer/pom.xml @@ -42,8 +42,8 @@ - mysql - mysql-connector-java + com.mysql + mysql-connector-j