From c12f871d3f9a6b60380f49b6b817832277e6d3fb Mon Sep 17 00:00:00 2001 From: Dando Date: Wed, 26 Jul 2023 12:10:23 +0800 Subject: [PATCH 01/47] Support sofa service duplicate configurable (#1204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * support SOFAService duplicate configurable * update to 3.20.0-SNAPSHOT * add unit test --------- Co-authored-by: 寻芳 --- pom.xml | 2 +- .../sofa/runtime/SofaRuntimeProperties.java | 11 +- .../SofaRuntimeConfigurationProperties.java | 8 + .../service/component/ServiceComponent.java | 5 + .../ServiceBeanFactoryPostProcessor.java | 7 +- .../runtime/test/ServiceDuplicateTest.java | 139 ++++++++++++++++++ .../test/beans/service/ClientFactoryBean.java | 42 ++++++ .../resources/sofa-boot/log-codes.properties | 1 + 8 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java create mode 100644 sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/beans/service/ClientFactoryBean.java diff --git a/pom.xml b/pom.xml index e27558c29..7f52c15c7 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.19.1 + 3.20.0-SNAPSHOT ${revision} 1.6.7 diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java index b3a20c589..8c215b13e 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java @@ -41,10 +41,9 @@ public class SofaRuntimeProperties { private static boolean jvmFilterEnable = false; private static boolean serviceInterfaceTypeCheck = false; private static boolean dynamicJvmServiceCacheEnable = false; - private static boolean serviceNameWithBeanId = false; - private static boolean referenceHealthCheckMoreDetailEnable = false; + private static boolean serviceCanBeDuplicate = true; public static boolean isManualReadinessCallback(ClassLoader classLoader) { return manualReadinessCallbackMap.get(classLoader) != null @@ -192,4 +191,12 @@ public static boolean isReferenceHealthCheckMoreDetailEnable() { public static void setReferenceHealthCheckMoreDetailEnable(boolean referenceHealthCheckMoreDetailEnable) { SofaRuntimeProperties.referenceHealthCheckMoreDetailEnable = referenceHealthCheckMoreDetailEnable; } + + public static boolean isServiceCanBeDuplicate() { + return serviceCanBeDuplicate; + } + + public static void setServiceCanBeDuplicate(boolean serviceCanBeDuplicate) { + SofaRuntimeProperties.serviceCanBeDuplicate = serviceCanBeDuplicate; + } } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/configure/SofaRuntimeConfigurationProperties.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/configure/SofaRuntimeConfigurationProperties.java index 7002e0d2a..1a6f416a8 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/configure/SofaRuntimeConfigurationProperties.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/configure/SofaRuntimeConfigurationProperties.java @@ -162,4 +162,12 @@ public void setReferenceHealthCheckMoreDetailEnable(boolean referenceHealthCheck .setReferenceHealthCheckMoreDetailEnable(referenceHealthCheckMoreDetailEnable); } + public boolean isServiceDuplicate() { + return SofaRuntimeProperties.isServiceCanBeDuplicate(); + } + + public void setServiceDuplicate(boolean serviceCanBeDuplicate) { + SofaRuntimeProperties.setServiceCanBeDuplicate(serviceCanBeDuplicate); + } + } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/component/ServiceComponent.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/component/ServiceComponent.java index cca7e10a8..f21826035 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/component/ServiceComponent.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/component/ServiceComponent.java @@ -315,4 +315,9 @@ public HealthResult isHealthy() { healthResult.setHealthReport(report); return healthResult; } + + @Override + public boolean canBeDuplicate() { + return SofaRuntimeProperties.isServiceCanBeDuplicate(); + } } 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 bc02f9e43..8b2eb940d 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 @@ -21,6 +21,7 @@ import com.alipay.sofa.boot.error.ErrorCode; import com.alipay.sofa.boot.util.BeanDefinitionUtil; import com.alipay.sofa.boot.util.SmartAnnotationUtils; +import com.alipay.sofa.runtime.SofaRuntimeProperties; import com.alipay.sofa.runtime.api.ServiceRuntimeException; import com.alipay.sofa.runtime.api.annotation.SofaReference; import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding; @@ -332,7 +333,11 @@ private void generateSofaServiceDefinition(String beanId, SofaService sofaServic builder.addDependsOn(beanId); registry.registerBeanDefinition(serviceId, builder.getBeanDefinition()); } else { - SofaLogger.warn("SofaService was already registered: {}", serviceId); + if (SofaRuntimeProperties.isServiceCanBeDuplicate()) { + SofaLogger.warn("SofaService was already registered: {}", serviceId); + } else { + throw new ServiceRuntimeException(ErrorCode.convert("01-00203", serviceId)); + } } } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java new file mode 100644 index 000000000..ab6191901 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.test; + +import com.alipay.sofa.boot.error.ErrorCode; +import com.alipay.sofa.runtime.api.ServiceRuntimeException; +import com.alipay.sofa.runtime.api.client.ServiceClient; +import com.alipay.sofa.runtime.api.client.param.ServiceParam; +import com.alipay.sofa.runtime.api.component.ComponentName; +import com.alipay.sofa.runtime.configure.SofaRuntimeConfigurationProperties; +import com.alipay.sofa.runtime.spi.util.ComponentNameFactory; +import com.alipay.sofa.runtime.spring.ServiceBeanFactoryPostProcessor; +import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; +import com.alipay.sofa.runtime.spring.factory.ServiceFactoryBean; +import com.alipay.sofa.runtime.test.beans.facade.SampleService; +import com.alipay.sofa.runtime.test.beans.service.ClientFactoryBean; +import com.alipay.sofa.runtime.test.beans.service.SofaServiceBeanService; +import com.alipay.sofa.runtime.test.configuration.RuntimeConfiguration; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +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.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import static com.alipay.sofa.runtime.service.component.ServiceComponent.SERVICE_COMPONENT_TYPE; + +/** + * ServiceDuplicateTest + * + * @author xunfang + * @version ServiceDuplicateTest.java, v 0.1 2023/7/24 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +@TestPropertySource(properties = { "spring.application.name=ServiceDuplicateTest", + "com.alipay.sofa.boot.service-duplicate=false" }) +@Import({ SofaServiceBeanService.class, ClientFactoryBean.class }) +public class ServiceDuplicateTest implements BeanFactoryAware { + @Autowired + private SampleService sofaServiceBeanService; + + @Autowired + private ClientFactoryBean clientFactoryBean; + + @Autowired + private ApplicationContext ctx; + + private BeanFactory beanFactory; + + @Autowired + private ServiceBeanFactoryPostProcessor serviceBeanFactoryPostProcessor; + + @Test + public void testAnnotationServiceDuplicate() { + SofaRuntimeConfigurationProperties configurationProperties = ctx + .getBean(SofaRuntimeConfigurationProperties.class); + Assert.assertFalse(configurationProperties.isServiceDuplicate()); + + String serviceId = SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, + "sofaServiceBeanService", null); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); + builder.getRawBeanDefinition().setBeanClass(ServiceFactoryBean.class); + ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition("duplicateService", + builder.getBeanDefinition()); + + try { + serviceBeanFactoryPostProcessor + .postProcessBeanFactory((ConfigurableListableBeanFactory) beanFactory); + throw new IllegalStateException("Service: " + serviceId + + " cannot be registered duplicate"); + } catch (ServiceRuntimeException e) { + Assert.assertTrue(e.getMessage().contains(ErrorCode.convert("01-00203", serviceId))); + } + + } + + @Test + public void testDynamicServiceDuplicate() { + SofaRuntimeConfigurationProperties configurationProperties = ctx + .getBean(SofaRuntimeConfigurationProperties.class); + Assert.assertFalse(configurationProperties.isServiceDuplicate()); + + ServiceClient serviceClient = clientFactoryBean.getClientFactory().getClient( + ServiceClient.class); + ServiceParam serviceParam = new ServiceParam(); + serviceParam.setInstance(sofaServiceBeanService); + serviceParam.setInterfaceType(SampleService.class); + serviceParam.setUniqueId("sofaServiceBeanService"); + ComponentName componentName = ComponentNameFactory.createComponentName( + SERVICE_COMPONENT_TYPE, SampleService.class, "sofaServiceBeanService"); + try { + serviceClient.service(serviceParam); + throw new IllegalStateException("Service: " + componentName + + " cannot be registered duplicate"); + } catch (ServiceRuntimeException e) { + Assert + .assertTrue(e.getMessage().contains(ErrorCode.convert("01-03002", componentName))); + } + + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @Import({ RuntimeConfiguration.class }) + static class ServiceBeanTestConfiguration { + + } +} diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/beans/service/ClientFactoryBean.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/beans/service/ClientFactoryBean.java new file mode 100644 index 000000000..a8c9aa47d --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/beans/service/ClientFactoryBean.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.runtime.test.beans.service; + +import com.alipay.sofa.runtime.api.aware.ClientFactoryAware; +import com.alipay.sofa.runtime.api.client.ClientFactory; +import org.springframework.stereotype.Component; + +/** + * ClientFactoryBean + * + * @author xunfang + * @version ClientFactoryBean.java, v 0.1 2023/7/24 + */ +@Component +public class ClientFactoryBean implements ClientFactoryAware { + + private ClientFactory clientFactory; + + @Override + public void setClientFactory(ClientFactory clientFactory) { + this.clientFactory = clientFactory; + } + + public ClientFactory getClientFactory() { + return clientFactory; + } +} diff --git a/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties b/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties index f6744ac73..7fc9c5365 100644 --- a/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties +++ b/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties @@ -21,6 +21,7 @@ 01-00200=Can not found binding converter for binding type %s 01-00201=Interface type is null. Interface type is required while publish a service 01-00202=Argument delay must be a positive integer or zero +01-00203=SofaService [%s] was already registered, please remove the duplicate registration 01-00400=JVM Reference[%s#%s] can not find the corresponding JVM service. Please check if there is a SOFA deployment publish the corresponding JVM service. If this exception occurred when the application starts up, please add Require-Module to SOFA deployment's MANIFEST.MF to indicate the startup dependency of SOFA modules ##Extension error From 92fc9fa6493df8e4dbb77c7e6e313572ec48b1bd Mon Sep 17 00:00:00 2001 From: Dando Date: Wed, 26 Jul 2023 14:34:11 +0800 Subject: [PATCH 02/47] Update spring boot 2.7.14 (#1205) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update spring boot to 2.7.14 --------- Co-authored-by: 寻芳 --- pom.xml | 2 +- .../com/alipay/sofa/runtime/test/ServiceDuplicateTest.java | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 7f52c15c7..16713a885 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.10 + 2.7.14 diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java index ab6191901..7c6835604 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ServiceDuplicateTest.java @@ -25,7 +25,6 @@ import com.alipay.sofa.runtime.spi.util.ComponentNameFactory; import com.alipay.sofa.runtime.spring.ServiceBeanFactoryPostProcessor; import com.alipay.sofa.runtime.spring.bean.SofaBeanNameGenerator; -import com.alipay.sofa.runtime.spring.factory.ServiceFactoryBean; import com.alipay.sofa.runtime.test.beans.facade.SampleService; import com.alipay.sofa.runtime.test.beans.service.ClientFactoryBean; import com.alipay.sofa.runtime.test.beans.service.SofaServiceBeanService; @@ -38,8 +37,6 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; @@ -84,10 +81,6 @@ public void testAnnotationServiceDuplicate() { String serviceId = SofaBeanNameGenerator.generateSofaServiceBeanName(SampleService.class, "sofaServiceBeanService", null); - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); - builder.getRawBeanDefinition().setBeanClass(ServiceFactoryBean.class); - ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition("duplicateService", - builder.getBeanDefinition()); try { serviceBeanFactoryPostProcessor From 312cd3292ab36e7c1ef4730b665a8373254e306c Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Mon, 7 Aug 2023 15:47:29 +0800 Subject: [PATCH 03/47] update ci config (#1219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 致节 --- .github/workflows/maven.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 4b7ce349c..224c63d43 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -5,9 +5,11 @@ name: CI on: push: - branches: [ master ] + branches: + - '**' pull_request: - branches: [ master ] + branches: + - '**' jobs: build: From e82792514b7793631ec59ffde5afd2e424d46f5b Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Mon, 7 Aug 2023 16:49:56 +0800 Subject: [PATCH 04/47] remove SearchStrategy#CURRENT (#1220) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 致节 --- .../startup/StartupHealthCheckStageConfiguration.java | 3 +-- .../startup/SofaStartupIsleAutoConfiguration.java | 5 ++--- .../SofaStartupHealthCheckAutoConfiguration.java | 3 +-- .../test/configuration/SofaStartupIsleAutoConfiguration.java | 5 ++--- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/startup/StartupHealthCheckStageConfiguration.java b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/startup/StartupHealthCheckStageConfiguration.java index c9c8fba47..fb98e97a9 100644 --- a/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/startup/StartupHealthCheckStageConfiguration.java +++ b/sofa-boot-project/sofa-boot-actuator-autoconfigure/src/main/java/com/alipay/sofa/boot/actuator/autoconfigure/startup/StartupHealthCheckStageConfiguration.java @@ -29,7 +29,6 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -45,7 +44,7 @@ public class StartupHealthCheckStageConfiguration { @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Bean - @ConditionalOnMissingBean(value = ReadinessCheckListener.class, search = SearchStrategy.CURRENT) + @ConditionalOnMissingBean(value = ReadinessCheckListener.class) public StartupReadinessCheckListener startupReadinessCheckListener(Environment environment, HealthCheckerProcessor healthCheckerProcessor, HealthIndicatorProcessor healthIndicatorProcessor, diff --git a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/startup/SofaStartupIsleAutoConfiguration.java b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/startup/SofaStartupIsleAutoConfiguration.java index a6b51be32..675313d66 100644 --- a/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/startup/SofaStartupIsleAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-autoconfigure/src/main/java/com/alipay/sofa/boot/autoconfigure/startup/SofaStartupIsleAutoConfiguration.java @@ -29,7 +29,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,7 +45,7 @@ public class SofaStartupIsleAutoConfiguration { @Bean - @ConditionalOnMissingBean(value = SpringContextInstallStage.class, search = SearchStrategy.CURRENT) + @ConditionalOnMissingBean(value = SpringContextInstallStage.class) public StartupSpringContextInstallStage startupSpringContextInstallStage(ApplicationContext applicationContext, SofaModuleProperties sofaModuleProperties, StartupReporter startupReporter) { @@ -55,7 +54,7 @@ public StartupSpringContextInstallStage startupSpringContextInstallStage(Applica } @Bean - @ConditionalOnMissingBean(value = ModelCreatingStage.class, search = SearchStrategy.CURRENT) + @ConditionalOnMissingBean(value = ModelCreatingStage.class) public StartupModelCreatingStage startupModelCreatingStage(ApplicationContext applicationContext, SofaModuleProperties sofaModuleProperties, SofaModuleProfileChecker sofaModuleProfileChecker, diff --git a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupHealthCheckAutoConfiguration.java b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupHealthCheckAutoConfiguration.java index 72215cc75..d133f1c8f 100644 --- a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupHealthCheckAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupHealthCheckAutoConfiguration.java @@ -27,7 +27,6 @@ import com.alipay.sofa.startup.stage.healthcheck.StartupReadinessCheckListener; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -41,7 +40,7 @@ public class SofaStartupHealthCheckAutoConfiguration { @Bean - @ConditionalOnMissingBean(value = ReadinessCheckListener.class, search = SearchStrategy.CURRENT) + @ConditionalOnMissingBean(value = ReadinessCheckListener.class) public StartupReadinessCheckListener startupReadinessCheckListener(Environment environment, HealthCheckerProcessor healthCheckerProcessor, HealthIndicatorProcessor healthIndicatorProcessor, diff --git a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupIsleAutoConfiguration.java b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupIsleAutoConfiguration.java index 1e76efb2a..04f99fe4a 100644 --- a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupIsleAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/test/java/com/alipay/sofa/startup/test/configuration/SofaStartupIsleAutoConfiguration.java @@ -40,7 +40,6 @@ import com.alipay.sofa.startup.test.stage.TestModelCreatingStage; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -59,7 +58,7 @@ public class SofaStartupIsleAutoConfiguration { @Bean - @ConditionalOnMissingBean(value = SpringContextInstallStage.class, search = SearchStrategy.CURRENT) + @ConditionalOnMissingBean(value = SpringContextInstallStage.class) public StartupSpringContextInstallStage startupSpringContextInstallStage(ApplicationContext applicationContext, SofaModuleProperties sofaModuleProperties, StartupReporter startupReporter) { @@ -68,7 +67,7 @@ public StartupSpringContextInstallStage startupSpringContextInstallStage(Applica } @Bean - @ConditionalOnMissingBean(value = ModelCreatingStage.class, search = SearchStrategy.CURRENT) + @ConditionalOnMissingBean(value = ModelCreatingStage.class) public StartupModelCreatingStage startupModelCreatingStage(ApplicationContext applicationContext, SofaModuleProperties sofaModuleProperties, SofaModuleProfileChecker sofaModuleProfileChecker, From a08cc7138dd45c8f5d647329d30297d54b99b4c5 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Mon, 7 Aug 2023 16:50:07 +0800 Subject: [PATCH 05/47] update sofa tracer 3.1.3 (#1217) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 致节 --- sofa-boot-project/sofaboot-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sofa-boot-project/sofaboot-dependencies/pom.xml b/sofa-boot-project/sofaboot-dependencies/pom.xml index c54208700..bdb971651 100644 --- a/sofa-boot-project/sofaboot-dependencies/pom.xml +++ b/sofa-boot-project/sofaboot-dependencies/pom.xml @@ -25,7 +25,7 @@ 5.4.2 - 3.1.0 + 3.1.3 5.8.3 From 43ec436e7ffc61822989e7580f582952d5aff9f9 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Mon, 7 Aug 2023 18:14:49 +0800 Subject: [PATCH 06/47] Support compatibility (#1221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add CompatibilityVerifier --------- Co-authored-by: 致节 --- ...VerifierApplicationContextInitializer.java | 92 +++++++++++++++++++ .../main/resources/META-INF/spring.factories | 3 +- ...fierApplicationContextInitializerTest.java | 72 +++++++++++++++ .../TestCompatibilityVerifier.java | 47 ++++++++++ .../test/resources/META-INF/spring.factories | 1 + .../AbstractJarVersionVerifier.java | 66 +++++++++++++ ...stractSwitchableCompatibilityVerifier.java | 61 ++++++++++++ .../CompatibilityNotMetException.java | 36 ++++++++ .../compatibility/CompatibilityPredicate.java | 33 +++++++ .../compatibility/CompatibilityVerifier.java | 33 +++++++ .../CompositeCompatibilityVerifier.java | 54 +++++++++++ .../compatibility/VerificationResult.java | 90 ++++++++++++++++++ .../AbstractJarVersionVerifierTest.java | 73 +++++++++++++++ ...ctSwitchableCompatibilityVerifierTest.java | 77 ++++++++++++++++ .../CompositeCompatibilityVerifierTest.java | 64 +++++++++++++ 15 files changed, 801 insertions(+), 1 deletion(-) create mode 100644 sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/compatibility/CompatibilityVerifierApplicationContextInitializer.java create mode 100644 sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java create mode 100644 sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/TestCompatibilityVerifier.java create mode 100644 sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/resources/META-INF/spring.factories create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractSwitchableCompatibilityVerifier.java create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityPredicate.java create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityVerifier.java create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.java create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java create mode 100644 sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractJarVersionVerifierTest.java create mode 100644 sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java create mode 100644 sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/compatibility/CompatibilityVerifierApplicationContextInitializer.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/compatibility/CompatibilityVerifierApplicationContextInitializer.java new file mode 100644 index 000000000..7d901ece9 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/compatibility/CompatibilityVerifierApplicationContextInitializer.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.runtime.compatibility; + +import com.alipay.sofa.boot.compatibility.CompatibilityVerifier; +import com.alipay.sofa.boot.compatibility.CompositeCompatibilityVerifier; +import com.alipay.sofa.runtime.log.SofaLogger; +import org.springframework.beans.BeanUtils; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.core.env.Environment; +import org.springframework.core.io.support.SpringFactoriesLoader; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * Implements for {@link ApplicationContextInitializer} to verify compatibilities. + * + * @author huzijie + * @version CompatibilityVerifierApplicationContextInitializer.java, v 0.1 2023年08月03日 4:44 PM huzijie Exp $ + */ +public class CompatibilityVerifierApplicationContextInitializer + implements + ApplicationContextInitializer { + + public static final String COMPATIBILITY_VERIFIER_ENABLED = "sofa.boot.compatibility-verifier.enabled"; + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + Environment environment = applicationContext.getEnvironment(); + if (!Boolean.parseBoolean(environment.getProperty(COMPATIBILITY_VERIFIER_ENABLED, "true"))) { + SofaLogger.info("Skip SOFABoot compatibility Verifier"); + return; + } + + // Load all CompatibilityVerifier and verify. + CompositeCompatibilityVerifier compositeCompatibilityVerifier = new CompositeCompatibilityVerifier( + getCompatibilityVerifierInstances(environment)); + compositeCompatibilityVerifier.verifyCompatibilities(); + } + + private List getCompatibilityVerifierInstances(Environment environment) { + // Use names and ensure unique to protect against duplicates + ClassLoader classLoader = this.getClass().getClassLoader(); + Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames( + CompatibilityVerifier.class, classLoader)); + List instances = createSpringFactoriesInstances( + CompatibilityVerifier.class, new Class[] { Environment.class }, classLoader, + new Object[] { environment }, names); + AnnotationAwareOrderComparator.sort(instances); + return instances; + } + + private List createSpringFactoriesInstances(Class type, Class[] parameterTypes, + ClassLoader classLoader, Object[] args, + Set names) { + List instances = new ArrayList<>(names.size()); + for (String name : names) { + try { + Class instanceClass = ClassUtils.forName(name, classLoader); + Assert.isAssignable(type, instanceClass); + Constructor constructor = instanceClass.getDeclaredConstructor(parameterTypes); + T instance = (T) BeanUtils.instantiateClass(constructor, args); + instances.add(instance); + } catch (Throwable ex) { + throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); + } + } + return instances; + } +} diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/resources/META-INF/spring.factories b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/resources/META-INF/spring.factories index 8a1b04916..f3026be4a 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/resources/META-INF/spring.factories +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/resources/META-INF/spring.factories @@ -1 +1,2 @@ -org.springframework.context.ApplicationContextInitializer=com.alipay.sofa.runtime.SofaRuntimeSpringContextInitializer +org.springframework.context.ApplicationContextInitializer=com.alipay.sofa.runtime.SofaRuntimeSpringContextInitializer,\ + com.alipay.sofa.runtime.compatibility.CompatibilityVerifierApplicationContextInitializer diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java new file mode 100644 index 000000000..1ae61738d --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.test.compatibility; + +import com.alipay.sofa.boot.compatibility.CompatibilityNotMetException; +import com.alipay.sofa.runtime.compatibility.CompatibilityVerifierApplicationContextInitializer; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.context.support.GenericXmlApplicationContext; +import org.springframework.mock.env.MockEnvironment; + +/** + * @author huzijie + * @version CompatibilityVerifierApplicationContextInitializerTest.java, v 0.1 2023年08月07日 12:11 PM huzijie Exp $ + */ +public class CompatibilityVerifierApplicationContextInitializerTest { + + private final MockEnvironment mockEnvironment = new MockEnvironment(); + + private final GenericApplicationContext applicationContext = new GenericXmlApplicationContext(); + + @Before + public void setUp() { + TestCompatibilityVerifier.invoked = false; + applicationContext.setEnvironment(mockEnvironment); + mockEnvironment.setProperty("enable.test.compatibility", "true"); + } + + @Test + public void enableKey() { + CompatibilityVerifierApplicationContextInitializer initializer = new CompatibilityVerifierApplicationContextInitializer(); + try { + initializer.initialize(applicationContext); + Assert.fail(); + } catch (CompatibilityNotMetException e) { + Assert.assertTrue(e.getMessage().contains( + "compatible = false, description = 'test error', action = 'test action'")); + } + + Assert.assertTrue(TestCompatibilityVerifier.invoked); + } + + @Test + public void disableKey() { + mockEnvironment.setProperty("sofa.boot.compatibility-verifier.enabled", "false"); + CompatibilityVerifierApplicationContextInitializer initializer = new CompatibilityVerifierApplicationContextInitializer(); + initializer.initialize(applicationContext); + + Assert.assertFalse(TestCompatibilityVerifier.invoked); + } + + @Before + public void clear() { + TestCompatibilityVerifier.invoked = false; + } +} diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/TestCompatibilityVerifier.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/TestCompatibilityVerifier.java new file mode 100644 index 000000000..399154bf7 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/TestCompatibilityVerifier.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.runtime.test.compatibility; + +import com.alipay.sofa.boot.compatibility.CompatibilityVerifier; +import com.alipay.sofa.boot.compatibility.VerificationResult; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; + +/** + * @author huzijie + * @version TestCompatibilityVerifier.java, v 0.1 2023年08月07日 12:19 PM huzijie Exp $ + */ +public class TestCompatibilityVerifier implements CompatibilityVerifier { + + public static boolean invoked = false; + + private final Environment environment; + + public TestCompatibilityVerifier(Environment environment) { + this.environment = environment; + } + + @Override + public VerificationResult verify() { + Assert.notNull(environment, "environment must not null"); + invoked = true; + if (!environment.containsProperty("enable.test.compatibility")) { + return VerificationResult.compatible(); + } + return VerificationResult.notCompatible("test error", "test action"); + } +} diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/resources/META-INF/spring.factories b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/resources/META-INF/spring.factories new file mode 100644 index 000000000..57fc4a6b9 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/resources/META-INF/spring.factories @@ -0,0 +1 @@ +com.alipay.sofa.boot.compatibility.CompatibilityVerifier=com.alipay.sofa.runtime.test.compatibility.TestCompatibilityVerifier \ No newline at end of file diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java new file mode 100644 index 000000000..ce7cd978e --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.compatibility; + +import org.springframework.core.env.Environment; + +import java.util.Collection; + +/** + * Abstract class for {@link AbstractSwitchableCompatibilityVerifier} to verify jar compatibility. + * + * @author huzijie + * @version AbstractJarVersionVerifier.java, v 0.1 2023年08月03日 5:14 PM huzijie Exp $ + */ +public abstract class AbstractJarVersionVerifier extends AbstractSwitchableCompatibilityVerifier { + + public AbstractJarVersionVerifier(Environment environment) { + super(environment); + } + + @Override + public CompatibilityPredicate compatibilityPredicate() { + return () -> { + Collection compatibilityPredicates = getJarCompatibilityPredicates(); + if (compatibilityPredicates == null) { + return true; + } + return compatibilityPredicates.stream().allMatch(CompatibilityPredicate::isCompatible); + }; + } + + @Override + public String errorDescription() { + return String + .format("SOFABoot is not compatible with jar [%s] for current version", name()); + } + + @Override + public String action() { + return String.format( + "Change [%s] to appropriate version.\n" + + "you can visit this doc [%s] and find an appropriate version.\n" + + "If you want to disable this check, just set the property [%s]=false]", + name(), doc(), this.enableKey); + } + + public abstract Collection getJarCompatibilityPredicates(); + + public abstract String name(); + + public abstract String doc(); +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractSwitchableCompatibilityVerifier.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractSwitchableCompatibilityVerifier.java new file mode 100644 index 000000000..a005ec28c --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractSwitchableCompatibilityVerifier.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.compatibility; + +import org.springframework.core.env.Environment; + +/** + * Abstract class for {@link CompatibilityVerifier} to support switch. + * + * @author huzijie + * @version AbstractSwitchableCompatibilityVerifier.java, v 0.1 2023年08月03日 6:10 PM huzijie Exp $ + */ +public abstract class AbstractSwitchableCompatibilityVerifier implements CompatibilityVerifier { + + private static final String ENABLE_KEY_FORMAT = "sofa.boot.compatibility-verifier.%s.enabled"; + + protected final Environment environment; + + protected String enableKey; + + public AbstractSwitchableCompatibilityVerifier(Environment environment) { + this.environment = environment; + } + + @Override + public VerificationResult verify() { + this.enableKey = String.format(ENABLE_KEY_FORMAT, enableKey()); + if (!Boolean.parseBoolean(environment.getProperty(enableKey, "true"))) { + return VerificationResult.compatible(); + } + + CompatibilityPredicate compatibilityPredicate = compatibilityPredicate(); + boolean matches = compatibilityPredicate.isCompatible(); + if (matches) { + return VerificationResult.compatible(); + } + return VerificationResult.notCompatible(errorDescription(), action()); + } + + public abstract CompatibilityPredicate compatibilityPredicate(); + + public abstract String errorDescription(); + + public abstract String action(); + + public abstract String enableKey(); +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java new file mode 100644 index 000000000..b9af17db2 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.compatibility; + +import java.util.Arrays; +import java.util.List; + +/** + * Exception for not compatibility met. + * + * @author huzijie + * @version CompatibilityNotMetException.java, v 0.1 2023年08月03日 4:40 PM huzijie Exp $ + */ +public class CompatibilityNotMetException extends RuntimeException { + + final List results; + + CompatibilityNotMetException(List results) { + super("Compatibility checks have failed: " + Arrays.toString(results.toArray())); + this.results = results; + } +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityPredicate.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityPredicate.java new file mode 100644 index 000000000..d5d43745b --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityPredicate.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.boot.compatibility; + +/** + * Interface for Predicate compatibility verify result, for form spring cloud. + * + * @author huzijie + * @version CompatibilityPredicate.java, v 0.1 2023年08月03日 4:35 PM huzijie Exp $ + */ +public interface CompatibilityPredicate { + + /** + * whether is compatible + * @return compatible result + */ + boolean isCompatible(); + +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityVerifier.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityVerifier.java new file mode 100644 index 000000000..6310da1ad --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityVerifier.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.boot.compatibility; + +/** + * Interface for compatibility verifier, for form spring cloud. + * + * @author huzijie + * @version CompatibilityVerifier.java, v 0.1 2023年08月03日 4:08 PM huzijie Exp $ + */ +public interface CompatibilityVerifier { + + /** + * verify compatibility + * @return verify result + */ + VerificationResult verify(); + +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.java new file mode 100644 index 000000000..e9cbc8d1f --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.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.compatibility; + +import java.util.ArrayList; +import java.util.List; + +/** + * Composite compatibility verifier. + * + * @author huzijie + * @version CompositeCompatibilityVerifier.java, v 0.1 2023年08月03日 4:40 PM huzijie Exp $ + */ +public class CompositeCompatibilityVerifier { + + private final List verifiers; + + public CompositeCompatibilityVerifier(List verifiers) { + this.verifiers = verifiers; + } + + public void verifyCompatibilities() { + List errors = verifierErrors(); + if (errors.isEmpty()) { + return; + } + throw new CompatibilityNotMetException(errors); + } + + private List verifierErrors() { + List errors = new ArrayList<>(); + for (CompatibilityVerifier verifier : this.verifiers) { + VerificationResult result = verifier.verify(); + if (result.isNotCompatible()) { + errors.add(result); + } + } + return errors; + } +} diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java new file mode 100644 index 000000000..b4d8b4f48 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.compatibility; + +import org.springframework.core.style.ToStringCreator; + +import java.util.Objects; + +/** + * Verification result. + * + * @author huzijie + * @version VerificationResult.java, v 0.1 2023年08月03日 4:08 PM huzijie Exp $ + */ +public class VerificationResult { + + private final boolean compatible; + + private final String description; + + private final String action; + + // if OK + private VerificationResult() { + this.compatible = true; + this.description = ""; + this.action = ""; + } + + // if not OK + private VerificationResult(String errorDescription, String action) { + this.compatible = false; + this.description = errorDescription; + this.action = action; + } + + public static VerificationResult compatible() { + return new VerificationResult(); + } + + public static VerificationResult notCompatible(String errorDescription, String action) { + return new VerificationResult(errorDescription, action); + } + + public boolean isNotCompatible() { + return !compatible; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VerificationResult)) { + return false; + } + VerificationResult that = (VerificationResult) o; + return compatible == that.compatible && description.equals(that.description) + && action.equals(that.action); + } + + @Override + public int hashCode() { + return Objects.hash(compatible, description, action); + } + + @Override + public String toString() { + ToStringCreator toStringCreator = new ToStringCreator(this); + toStringCreator.append("compatible", compatible); + toStringCreator.append("description", description); + toStringCreator.append("action", action); + return toStringCreator.toString(); + } + +} diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractJarVersionVerifierTest.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractJarVersionVerifierTest.java new file mode 100644 index 000000000..e4ffd03a0 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractJarVersionVerifierTest.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.test.compatibility; + +import com.alipay.sofa.boot.compatibility.AbstractJarVersionVerifier; +import com.alipay.sofa.boot.compatibility.CompatibilityPredicate; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.env.Environment; +import org.springframework.mock.env.MockEnvironment; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author huzijie + * @version AbstractJarVersionVerifierTest.java, v 0.1 2023年08月07日 12:07 PM huzijie Exp $ + */ +public class AbstractJarVersionVerifierTest { + + private final MockEnvironment mockEnvironment = new MockEnvironment(); + + @Test + public void testJar() { + TestJarVersionVerifier verifier = new TestJarVersionVerifier(mockEnvironment); + Assert.assertTrue(verifier.verify().isNotCompatible()); + } + + public static class TestJarVersionVerifier extends AbstractJarVersionVerifier { + + public TestJarVersionVerifier(Environment environment) { + super(environment); + } + + @Override + public Collection getJarCompatibilityPredicates() { + List list = new ArrayList<>(); + list.add(() -> false); + list.add(() -> true); + return list; + } + + @Override + public String name() { + return "test jar"; + } + + @Override + public String doc() { + return "test doc"; + } + + @Override + public String enableKey() { + return "test"; + } + } +} diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java new file mode 100644 index 000000000..28210867a --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.test.compatibility; + +import com.alipay.sofa.boot.compatibility.AbstractSwitchableCompatibilityVerifier; +import com.alipay.sofa.boot.compatibility.CompatibilityPredicate; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.env.Environment; +import org.springframework.mock.env.MockEnvironment; + +/** + * @author huzijie + * @version AbstractSwitchableCompatibilityVerifierTest.java, v 0.1 2023年08月07日 11:56 AM huzijie Exp $ + */ +public class AbstractSwitchableCompatibilityVerifierTest { + + private final MockEnvironment mockEnvironment = new MockEnvironment(); + + @Test + public void enableKey() { + mockEnvironment.setProperty("sofa.boot.compatibility-verifier.test.enabled", "true"); + TestSwitchableCompatibilityVerifier verifier = new TestSwitchableCompatibilityVerifier( + mockEnvironment); + Assert.assertTrue(verifier.verify().isNotCompatible()); + } + + @Test + public void disableKey() { + mockEnvironment.setProperty("sofa.boot.compatibility-verifier.test.enabled", "false"); + TestSwitchableCompatibilityVerifier verifier = new TestSwitchableCompatibilityVerifier( + mockEnvironment); + Assert.assertFalse(verifier.verify().isNotCompatible()); + } + + public static class TestSwitchableCompatibilityVerifier extends + AbstractSwitchableCompatibilityVerifier { + + public TestSwitchableCompatibilityVerifier(Environment environment) { + super(environment); + } + + @Override + public CompatibilityPredicate compatibilityPredicate() { + return () -> false; + } + + @Override + public String errorDescription() { + return ""; + } + + @Override + public String action() { + return ""; + } + + @Override + public String enableKey() { + return "test"; + } + } +} diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java new file mode 100644 index 000000000..66975c035 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.boot.test.compatibility; + +import com.alipay.sofa.boot.compatibility.CompatibilityNotMetException; +import com.alipay.sofa.boot.compatibility.CompatibilityVerifier; +import com.alipay.sofa.boot.compatibility.CompositeCompatibilityVerifier; +import com.alipay.sofa.boot.compatibility.VerificationResult; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author huzijie + * @version CompositeCompatibilityVerifierTest.java, v 0.1 2023年08月07日 12:00 PM huzijie Exp $ + */ +public class CompositeCompatibilityVerifierTest { + + @Test + public void empty() { + CompositeCompatibilityVerifier verifier = new CompositeCompatibilityVerifier( + new ArrayList<>()); + verifier.verifyCompatibilities(); + } + + @Test + public void pass() { + CompatibilityVerifier compatibilityVerifier = VerificationResult::compatible; + List verifiers = new ArrayList<>(); + verifiers.add(compatibilityVerifier); + CompositeCompatibilityVerifier verifier = new CompositeCompatibilityVerifier(verifiers); + verifier.verifyCompatibilities(); + } + + @Test + public void notPass() { + CompatibilityVerifier compatibilityVerifier = () -> VerificationResult.notCompatible("verify error", "do action"); + List verifiers = new ArrayList<>(); + verifiers.add(compatibilityVerifier); + CompositeCompatibilityVerifier verifier = new CompositeCompatibilityVerifier(verifiers); + try { + verifier.verifyCompatibilities(); + Assert.fail(); + } catch (CompatibilityNotMetException e) { + Assert.assertTrue(e.getMessage().contains("description = 'verify error', action = 'do action'")); + } + } +} From b8593ea52cccd6ce08d749810a5f9919c4fdb9f5 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Tue, 8 Aug 2023 11:17:05 +0800 Subject: [PATCH 07/47] update error logs (#1222) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 致节 --- .../src/main/resources/sofa-boot/log-codes.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties b/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties index 7fc9c5365..29bd089a8 100644 --- a/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties +++ b/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties @@ -2,7 +2,7 @@ #Runtime error ##SOFA service error -01-00000=Must contains the target object whiling registering Service +01-00000=Must contain the target object whiling registering Service 01-00001=Can't find BindingAdapter of type %s while registering service %s 01-00002=PreOut Binding [%s] for [%s] occur exception 01-00003=PreOut Binding [%s] occur exception @@ -18,7 +18,7 @@ 01-00102=Unable to get implementation of reference component, there's some error occurred when register this reference component 01-00103=Bean [%s] of type [%s] has already annotated by @SofaService can not be registered using xml. Please check it 01-00104=Bean [%s] type is [%s] not isAssignableFrom [%s] , please check it -01-00200=Can not found binding converter for binding type %s +01-00200=Can not find binding converter for binding type %s 01-00201=Interface type is null. Interface type is required while publish a service 01-00202=Argument delay must be a positive integer or zero 01-00203=SofaService [%s] was already registered, please remove the duplicate registration @@ -59,7 +59,7 @@ 01-11004=Interrupted when wait for Spring Application Context refresh 01-11005=Error occurred when get Spring Application Context refresh future 01-11006=Cannot register module deployment for module name '[%s]': replacing '[%s]' with '[%s]' -01-11007=Some module context(s) %s failed to refresh, please see /logs/sofa-runtime/common-error.log for further information, you could set 'com.alipay.sofa.boot.ignoreModuleInstallFailure=true' to ignore it +01-11007=Some module context(s) %s failed to refresh, please see /logs/sofa-runtime/common-error.log to find root cause ##Module dependency error 01-12000=Modules that could not install(Mainly due to module dependency not satisfied) From 3a21e03bc3ed49d8fb2f8d141ef86cd79c4569b4 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Tue, 8 Aug 2023 14:14:29 +0800 Subject: [PATCH 08/47] Update compatibility not met message (#1223) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update logs * update logs --------- Co-authored-by: 致节 --- ...fierApplicationContextInitializerTest.java | 4 +- .../AbstractJarVersionVerifier.java | 10 ++--- .../CompatibilityNotMetException.java | 5 +-- .../CompositeCompatibilityVerifier.java | 4 +- .../compatibility/VerificationResult.java | 40 +++++++++---------- ...ctSwitchableCompatibilityVerifierTest.java | 4 +- .../CompositeCompatibilityVerifierTest.java | 3 +- 7 files changed, 35 insertions(+), 35 deletions(-) diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java index 1ae61738d..99bedcb20 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/compatibility/CompatibilityVerifierApplicationContextInitializerTest.java @@ -49,8 +49,8 @@ public void enableKey() { initializer.initialize(applicationContext); Assert.fail(); } catch (CompatibilityNotMetException e) { - Assert.assertTrue(e.getMessage().contains( - "compatible = false, description = 'test error', action = 'test action'")); + Assert.assertTrue(e.getMessage().contains("description: test error")); + Assert.assertTrue(e.getMessage().contains("action: test action")); } Assert.assertTrue(TestCompatibilityVerifier.invoked); diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java index ce7cd978e..bb51de040 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/AbstractJarVersionVerifier.java @@ -45,16 +45,16 @@ public CompatibilityPredicate compatibilityPredicate() { @Override public String errorDescription() { - return String - .format("SOFABoot is not compatible with jar [%s] for current version", name()); + return String.format("SOFABoot is not compatible with jar [%s] for current version.", + name()); } @Override public String action() { return String.format( - "Change [%s] to appropriate version.\n" - + "you can visit this doc [%s] and find an appropriate version.\n" - + "If you want to disable this check, just set the property [%s]=false]", + "Change [%s] to appropriate version," + + "you can visit this doc [%s] and find an appropriate version," + + "If you want to disable this check, just set the property [%s=false].", name(), doc(), this.enableKey); } diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java index b9af17db2..c9dc5778d 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompatibilityNotMetException.java @@ -16,7 +16,6 @@ */ package com.alipay.sofa.boot.compatibility; -import java.util.Arrays; import java.util.List; /** @@ -29,8 +28,8 @@ public class CompatibilityNotMetException extends RuntimeException { final List results; - CompatibilityNotMetException(List results) { - super("Compatibility checks have failed: " + Arrays.toString(results.toArray())); + CompatibilityNotMetException(List results, String errorMessage) { + super("Compatibility checks have failed: " + errorMessage); this.results = results; } } diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.java index e9cbc8d1f..b1d6d382c 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/CompositeCompatibilityVerifier.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** * Composite compatibility verifier. @@ -38,7 +39,8 @@ public void verifyCompatibilities() { if (errors.isEmpty()) { return; } - throw new CompatibilityNotMetException(errors); + String errorMessage = errors.stream().map(VerificationResult::toErrorMessage).collect(Collectors.toList()).toString(); + throw new CompatibilityNotMetException(errors, errorMessage); } private List verifierErrors() { diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java index b4d8b4f48..b011c26bf 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/compatibility/VerificationResult.java @@ -16,7 +16,7 @@ */ package com.alipay.sofa.boot.compatibility; -import org.springframework.core.style.ToStringCreator; +import org.springframework.util.StringUtils; import java.util.Objects; @@ -28,22 +28,18 @@ */ public class VerificationResult { - private final boolean compatible; + private final String description; - private final String description; - - private final String action; + private final String action; // if OK private VerificationResult() { - this.compatible = true; this.description = ""; this.action = ""; } // if not OK private VerificationResult(String errorDescription, String action) { - this.compatible = false; this.description = errorDescription; this.action = action; } @@ -57,7 +53,20 @@ public static VerificationResult notCompatible(String errorDescription, String a } public boolean isNotCompatible() { - return !compatible; + return StringUtils.hasText(this.description) || StringUtils.hasText(this.action); + } + + public String toErrorMessage() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("\n"); + stringBuilder.append("VerificationResult:"); + stringBuilder.append("\n"); + stringBuilder.append("—— description: "); + stringBuilder.append(description); + stringBuilder.append("\n"); + stringBuilder.append("—— action: "); + stringBuilder.append(action); + return stringBuilder.toString(); } @Override @@ -69,22 +78,11 @@ public boolean equals(Object o) { return false; } VerificationResult that = (VerificationResult) o; - return compatible == that.compatible && description.equals(that.description) - && action.equals(that.action); + return description.equals(that.description) && action.equals(that.action); } @Override public int hashCode() { - return Objects.hash(compatible, description, action); + return Objects.hash(description, action); } - - @Override - public String toString() { - ToStringCreator toStringCreator = new ToStringCreator(this); - toStringCreator.append("compatible", compatible); - toStringCreator.append("description", description); - toStringCreator.append("action", action); - return toStringCreator.toString(); - } - } diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java index 28210867a..7ab83c77a 100644 --- a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/AbstractSwitchableCompatibilityVerifierTest.java @@ -61,12 +61,12 @@ public CompatibilityPredicate compatibilityPredicate() { @Override public String errorDescription() { - return ""; + return "fafa"; } @Override public String action() { - return ""; + return "fafa"; } @Override diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java index 66975c035..17694b06b 100644 --- a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/test/compatibility/CompositeCompatibilityVerifierTest.java @@ -58,7 +58,8 @@ public void notPass() { verifier.verifyCompatibilities(); Assert.fail(); } catch (CompatibilityNotMetException e) { - Assert.assertTrue(e.getMessage().contains("description = 'verify error', action = 'do action'")); + Assert.assertTrue(e.getMessage().contains("description: verify error")); + Assert.assertTrue(e.getMessage().contains("action: do action")); } } } From 9c9a0b973b72b8b58ab1ce030efb1e4ed24942ab Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Fri, 11 Aug 2023 10:23:37 +0800 Subject: [PATCH 09/47] ensure threadlocal object remove after use (#1225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 致节 --- .../com/alipay/sofa/isle/stage/ModuleLogOutputStage.java | 1 + .../sofa/runtime/factory/BeanLoadCostBeanFactory.java | 4 ++++ .../sofa/runtime/invoke/DynamicJvmServiceProxyFinder.java | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/isle/stage/ModuleLogOutputStage.java b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/isle/stage/ModuleLogOutputStage.java index 9e9b47724..574e7bef8 100644 --- a/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/isle/stage/ModuleLogOutputStage.java +++ b/sofa-boot-project/sofa-boot-core/isle-sofa-boot/src/main/java/com/alipay/sofa/isle/stage/ModuleLogOutputStage.java @@ -147,6 +147,7 @@ private void logInfoBeanCost(StringBuilder stringBuilder, List interfaceType; + /** interface class type name*/ + protected String interfaceTypeCanonicalName; /** interface mode */ protected InterfaceMode interfaceMode = InterfaceMode.spring; /** properties of contract */ @@ -50,6 +52,7 @@ protected AbstractContract(String uniqueId, Class interfaceType) { this.uniqueId = uniqueId; } this.interfaceType = interfaceType; + this.interfaceTypeCanonicalName = interfaceType.getCanonicalName(); } protected AbstractContract(String uniqueId, Class interfaceType, InterfaceMode interfaceMode) { @@ -114,4 +117,9 @@ public String getProperty(String key) { public Map getProperty() { return property; } + + @Override + public String getInterfaceTypeCanonicalName() { + return interfaceTypeCanonicalName; + } } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/binding/Contract.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/binding/Contract.java index 3a1dcd2cb..7b907a6d1 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/binding/Contract.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/binding/Contract.java @@ -99,4 +99,12 @@ public interface Contract { * @param bindings binding objects */ void addBinding(Set bindings); + + /** + * get interface type class canonical name + * @return get interface type class canonical name + */ + default String getInterfaceTypeCanonicalName() { + return getInterfaceType().getCanonicalName(); + } } diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/SofaEventHandlerTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/SofaEventHandlerTest.java index 245ec27e5..2a569f171 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/SofaEventHandlerTest.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/SofaEventHandlerTest.java @@ -43,7 +43,6 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.context.ConfigurableApplicationContext; @@ -127,7 +126,7 @@ public void testDynamicProxyFinder() throws Exception { Mockito.when(sofaRuntimeManager.getComponentManager()).thenReturn(((SofaRuntimeContext) ctx.getBean("sofaRuntimeContext")) .getComponentManager()); - Mockito.when(contract.getInterfaceType()).thenAnswer((Answer) invocationOnMock -> SampleService.class); + Mockito.when(contract.getInterfaceTypeCanonicalName()).thenReturn("com.alipay.sofa.runtime.test.beans.facade.SampleService"); Mockito.when(contract.getUniqueId()).thenReturn(""); Mockito.when(contract.getBinding(JvmBinding.JVM_BINDING_TYPE)).thenReturn(new JvmBinding()); Mockito.when(invocation.getArguments()).thenReturn(new Object[] {}); From 1406c1b516bad332e8174f19552db5ccdbdb0b69 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Fri, 25 Aug 2023 17:18:54 +0800 Subject: [PATCH 12/47] Merge inject mock (#1237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * cherrypick:Added side-effect-free stubbing framework for SOFABoot applications #1224 --------- Co-authored-by: 致节 --- .../sofa-boot-core/test-sofa-boot/pom.xml | 23 + .../InjectorMockTestExecutionListener.java | 130 ++++++ .../injector/annotation/MockBeanInjector.java | 152 +++++++ .../injector/annotation/SpyBeanInjector.java | 143 +++++++ .../mock/injector/definition/Definition.java | 132 ++++++ .../injector/definition/MockDefinition.java | 129 ++++++ .../definition/QualifierDefinition.java | 112 +++++ .../injector/definition/SpyDefinition.java | 123 ++++++ .../injector/parser/DefinitionParser.java | 118 ++++++ .../resolver/BeanInjectorResolver.java | 214 ++++++++++ .../injector/resolver/BeanInjectorStub.java | 84 ++++ .../main/resources/META-INF/spring.factories | 2 + ...ectorMockApplicationContextCacheTests.java | 122 ++++++ ...njectorMockTestExecutionListenerTests.java | 166 ++++++++ .../definition/MockDefinitionTests.java | 101 +++++ .../definition/QualifierDefinitionTests.java | 164 +++++++ .../definition/SpyDefinitionTests.java | 114 +++++ .../example/ExampleExtraInterface.java | 26 ++ .../mock/injector/example/ExampleService.java | 28 ++ .../example/ExampleServiceCaller.java | 45 ++ .../ExampleServiceCallerInterface.java | 29 ++ .../injector/example/RealExampleService.java | 41 ++ .../InjectMockToAopProxyBeanTests.java | 79 ++++ .../InjectMockToFactoryBeanTests.java | 91 ++++ ...InjectMockToGenericBeanExtensionTests.java | 28 ++ .../InjectMockToGenericBeanTestBase.java | 92 ++++ .../InjectMockToNormalBeanTests.java | 69 +++ .../InjectSpyToAopProxyBeanTests.java | 83 ++++ .../InjectSpyToFactoryBeanTests.java | 97 +++++ .../InjectSpyToNormalBeanTests.java | 72 ++++ .../InjectSpyWithJdkProxyTests.java | 77 ++++ .../integration/TestSofaBootApplication.java | 33 ++ .../parser/DefinitionParserTests.java | 140 ++++++ .../resolver/BeanInjectorResolverTests.java | 400 ++++++++++++++++++ .../resolver/BeanInjectorStubTests.java | 98 +++++ .../resources/config/application.properties | 4 + .../src/test/resources/logback.xml | 16 + 37 files changed, 3577 insertions(+) create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/MockBeanInjector.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/SpyBeanInjector.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/Definition.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/MockDefinition.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinition.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinition.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParser.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/resources/META-INF/spring.factories create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockApplicationContextCacheTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListenerTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/MockDefinitionTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinitionTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinitionTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleExtraInterface.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleService.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCaller.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCallerInterface.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/RealExampleService.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToAopProxyBeanTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToFactoryBeanTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanExtensionTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanTestBase.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToNormalBeanTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToAopProxyBeanTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToFactoryBeanTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToNormalBeanTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyWithJdkProxyTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/TestSofaBootApplication.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParserTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolverTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStubTests.java create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/config/application.properties create mode 100644 sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/logback.xml 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 699b899a5..9f93a77be 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 @@ -29,12 +29,35 @@ provided + + org.mockito + mockito-core + provided + + + + com.alipay.sofa + isle-sofa-boot + true + + junit junit provided + + org.springframework.boot + spring-boot-starter-test + test + + + + org.assertj + assertj-core + test + diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.java new file mode 100644 index 000000000..98d01ab71 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.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.test.mock.injector; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import com.alipay.sofa.test.mock.injector.definition.Definition; +import com.alipay.sofa.test.mock.injector.parser.DefinitionParser; +import com.alipay.sofa.test.mock.injector.resolver.BeanInjectorResolver; +import com.alipay.sofa.test.mock.injector.resolver.BeanInjectorStub; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.Ordered; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.support.AbstractTestExecutionListener; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; + +/** + * {@link TestExecutionListener} to enable {@link MockBeanInjector} and + * {@link SpyBeanInjector} support. + * + * @author pengym + * @version InjectorMockTestExecutionListener.java, v 0.1 2023年08月07日 15:51 pengym + */ +public class InjectorMockTestExecutionListener extends AbstractTestExecutionListener { + + static final String STUBBED_FIELDS = "_SOFA_BOOT_STUBBED_FIELDS"; + + static final String STUBBED_DEFINITIONS = "_SOFA_BOOT_STUBBED_DEFINITIONS"; + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + + @Override + public void prepareTestInstance(TestContext testContext) { + // parse annotations and inject fields + injectFields(testContext); + } + + private void injectFields(TestContext testContext) { + // create definitions form annotation + DefinitionParser parser = new DefinitionParser(); + parser.parse(testContext.getTestClass()); + testContext.setAttribute(STUBBED_DEFINITIONS, parser.getDefinitions()); + + // create stubs form definitions + Collection beanInjectorStubs = createStubs(parser, testContext); + testContext.setAttribute(STUBBED_FIELDS, beanInjectorStubs); + + // inject mock/spy to test class fields + injectTestClass(parser, testContext); + } + + private void injectTestClass(DefinitionParser parser, TestContext testContext) { + parser.getDefinitions().forEach(definition -> { + Field field = parser.getField(definition); + if (field != null) { + Object target = testContext.getTestInstance(); + ReflectionUtils.makeAccessible(field); + Object existingValue = ReflectionUtils.getField(field, target); + Object injectValue = definition.getMockInstance(); + if (existingValue == injectValue) { + return; + } + Assert.state(existingValue == null, () -> "The existing value '" + existingValue + "' of field '" + field + + "' is not the same as the new value '" + injectValue + "'"); + ReflectionUtils.setField(field, target, injectValue); + } + }); + } + + private Collection createStubs(DefinitionParser parser, + TestContext testContext) { + Collection beanInjectorStubs = new ArrayList<>(); + BeanInjectorResolver resolver = new BeanInjectorResolver( + testContext.getApplicationContext()); + for (Definition definition : parser.getDefinitions()) { + BeanInjectorStub field = resolver.resolveStub(definition); + if (field != null) { + beanInjectorStubs.add(field); + } + } + return beanInjectorStubs; + } + + @Override + @SuppressWarnings("unchecked") + public void beforeTestMethod(TestContext testContext) { + Set stubbedDefinitions = (Set) testContext.getAttribute(STUBBED_DEFINITIONS); + if (!CollectionUtils.isEmpty(stubbedDefinitions)) { + stubbedDefinitions.stream().filter(definition -> definition.getReset().equals(MockReset.BEFORE)).forEach(Definition::resetMock); + } + } + + @Override + @SuppressWarnings("unchecked") + public void afterTestMethod(TestContext testContext) { + Set stubbedDefinitions = (Set) testContext.getAttribute(STUBBED_DEFINITIONS); + if (!CollectionUtils.isEmpty(stubbedDefinitions)) { + stubbedDefinitions.stream().filter(definition -> definition.getReset().equals(MockReset.AFTER)).forEach(Definition::resetMock); + } + Collection beanStubbedFields = (Collection) testContext.getAttribute(STUBBED_FIELDS); + if (!CollectionUtils.isEmpty(beanStubbedFields)) { + beanStubbedFields.forEach(BeanInjectorStub::reset); + } + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/MockBeanInjector.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/MockBeanInjector.java new file mode 100644 index 000000000..fc020f311 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/MockBeanInjector.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.annotation; + +import org.mockito.Answers; +import org.mockito.MockSettings; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that can be used to create mocks and inject mock to a target bean's field. + *

+ * Injector target bean can be found by type or by {@link #name() bean name}. When registered by + * type, any existing single bean of a matching type (including subclasses) in the context + * will be found for injector. If no suitable bean could be found, {@link IllegalStateException} will be thrown. + *

+ * Field in target bean will be found by {@link #field()}. If no field could be found, {@link IllegalStateException} will be thrown. + *

+ * + * Typical usage might be:

+ * @RunWith(SpringRunner.class)
+ * public class ExampleServiceTest {
+ *
+ *      @Autowired
+ *      private ExampleService service;
+ *
+ *      @MockBeanInjector(type = ExampleService.class, field = "fieldA")
+ *      private FieldAClass mock;
+ *
+ *      @Test
+ *      public void testInjectExampleServiceFieldA() {
+ *          // 1. mock external dependency
+ *          given(mock.callSomeMethod(...))
+ *              .willReturn(...);
+ *
+ *          // 2. perform testing
+ *          service.doSomething();
+ *
+ *          // 3. behavioral-driven testing / standard unit-testing
+ *          then(mock)
+ *              .should(atLeastOnce())
+ *              .callSomeMethod(...);
+ *
+ *          assertThat(...)...;
+ *      }
+ *
+ *      #064;Configuration
+ *      @Import(ExampleService.class) // A @Component injected with ExampleService
+ *      static class Config {
+ *      }
+ * }
+ * 
+ * If there is more than one bean of the requested type, qualifier metadata must be + * specified at field level:
+ * @RunWith(SpringRunner.class)
+ * public class ExampleTests {
+ *
+ *     @MockBeanInjector(type = ExampleService.class, field = "fieldA")
+ *     @Qualifier("example")
+ *     private ExampleService service;
+ *
+ *     ...
+ * }
+ * 
+ * @author pengym + * @version MockBeanInjector.java, v 0.1 2023年08月07日 15:32 pengym + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MockBeanInjector { + + /** + * The name for field which should inject the mock. + *

When can not find the target field, an {@link IllegalStateException} will be thrown. + */ + String field(); + + /** + * The name of the bean to inject the mock to a field. + * @return the name of the target bean + */ + String name() default ""; + + /** + * The class type of the bean to inject the mock to a field. This is an alias of {@link #type()} which can be used for + * brevity if no other attributes are defined. See {@link #type()} for details. + * @return the class ype of the target bean + */ + @AliasFor("type") + Class value() default void.class; + + /** + * The class type of the bean to inject the mock to a field + * @return the class ype of the target bean + */ + @AliasFor("value") + Class type() default void.class; + + /** + * The application context id to find the target bean. If not specified, the root application context will be used. + *

When can not find the target SOFA module for the specified module name, an {@link IllegalStateException} will be thrown. + */ + String module() default ""; + + /** + * Any extra interfaces that should also be declared on the mock. See + * {@link MockSettings#extraInterfaces(Class...)} for details. + * @return any extra interfaces + */ + Class[] extraInterfaces() default {}; + + /** + * The {@link Answers} type to use on the mock. + * @return the answer type + */ + Answers answer() default Answers.RETURNS_DEFAULTS; + + /** + * If the generated mock is serializable. See {@link MockSettings#serializable()} for + * details. + * @return if the mock is serializable + */ + boolean serializable() default false; + + /** + * The reset mode to apply to the mock. The default is {@link MockReset#AFTER} + * meaning that mocks are automatically reset after each test method is invoked. + * @return the reset mode + */ + MockReset reset() default MockReset.AFTER; +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/SpyBeanInjector.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/SpyBeanInjector.java new file mode 100644 index 000000000..94b3e76fa --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/annotation/SpyBeanInjector.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.annotation; + +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that can be used to create spies and inject spy to a target bean's field. + *

+ * Injector target bean can be found by type or by {@link #name() bean name}. When registered by + * type, any existing single bean of a matching type (including subclasses) in the context + * will be found for injector. If no suitable bean could be found, {@link IllegalStateException} will be thrown. + *

+ * Field in target bean will be found by {@link #field()}. If no field could be found, {@link IllegalStateException} will be thrown. + *

+ * + * Typical usage might be:

+ * @RunWith(SpringRunner.class)
+ * public class ExampleServiceTest {
+ *
+ *      @Autowired
+ *      private ExampleService service;
+ *
+ *      @SpyBeanInjector(type = ExampleService.class, field = "fieldA")
+ *      private FieldAClass spy;
+ *
+ *      @Test
+ *      public void testInjectExampleServiceFieldA() {
+ *          // 1. spy external dependency
+ *          given(spy.callSomeMethod(...))
+ *              .willReturn(...);
+ *
+ *          // 2. perform testing
+ *          service.doSomething();
+ *
+ *          // 3. behavioral-driven testing / standard unit-testing
+ *          then(spy)
+ *              .should(atLeastOnce())
+ *              .callSomeMethod(...);
+ *
+ *          assertThat(...)...;
+ *      }
+ *
+ *      #064;Configuration
+ *      @Import(ExampleService.class) // A @Component injected with ExampleService
+ *      static class Config {
+ *      }
+ * }
+ * 
+ * If there is more than one bean of the requested type, qualifier metadata must be + * specified at field level:
+ * @RunWith(SpringRunner.class)
+ * public class ExampleTests {
+ *
+ *     @SpyBeanInjector(type = ExampleService.class, field = "fieldA")
+ *     @Qualifier("example")
+ *     private ExampleService service;
+ *
+ *     ...
+ * }
+ * 
+ * @author pengym + * @version SpyBeanInjector.java, v 0.1 2023年08月07日 15:38 pengym + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SpyBeanInjector { + + /** + * The name for field which should inject the spy. + *

When can not find the target field, an {@link IllegalStateException} will be thrown. + */ + String field(); + + /** + * The name of the bean to inject the spy to a field. + * @return the name of the target bean + */ + String name() default ""; + + /** + * The class type of the bean to inject the spy to a field. This is an alias of {@link #type()} which can be used for + * brevity if no other attributes are defined. See {@link #type()} for details. + * @return the class type of the target bean + */ + @AliasFor("type") + Class value() default void.class; + + /** + * The class type of the bean to inject the spy to a field + * @return the class ype of the target bean + */ + @AliasFor("value") + Class type() default void.class; + + /** + * The application context id to find the target bean. If not specified, the root application context will be used. + *

When can not find the target SOFA module for the specified module name, an {@link IllegalStateException} will be thrown. + */ + String module() default ""; + + /** + * The reset mode to apply to the spy. The default is {@link MockReset#AFTER} + * meaning that spies are automatically reset after each test method is invoked. + * @return the reset mode + */ + MockReset reset() default MockReset.AFTER; + + /** + * Indicates that Mockito methods such as {@link Mockito#verify(Object) verify(mock)} + * should use the {@code target} of AOP advised beans, rather than the proxy itself. + * If set to {@code false} you may need to use the result of + * {@link org.springframework.test.util.AopTestUtils#getUltimateTargetObject(Object) + * AopTestUtils.getUltimateTargetObject(...)} when calling Mockito methods. + * @return {@code true} if the target of AOP advised beans is used or {@code false} if + * the proxy is used directly + */ + boolean proxyTargetAware() default true; + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/Definition.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/Definition.java new file mode 100644 index 000000000..1a6effac5 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/Definition.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.definition; + +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.ResolvableType; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + * Base class for {@link MockDefinition} or {@link SpyDefinition} + * + * @author pengym + */ +public abstract class Definition { + + private static final int MULTIPLIER = 31; + + private final String name; + + private final ResolvableType type; + + private final String module; + + private final String field; + + private final MockReset reset; + + private final QualifierDefinition qualifier; + + private final ResolvableType mockType; + + protected Object mockInstance; + + public Definition(ResolvableType mockType, String name, ResolvableType type, String module, + String field, MockReset reset, QualifierDefinition qualifier) { + Assert.notNull(mockType, "MockType must not be null"); + this.mockType = mockType; + this.type = type; + this.name = name; + this.module = module; + this.field = field; + this.reset = (reset != null) ? reset : MockReset.AFTER; + this.qualifier = qualifier; + } + + public String getName() { + return name; + } + + public ResolvableType getType() { + return type; + } + + public String getModule() { + return module; + } + + public String getField() { + return field; + } + + public MockReset getReset() { + return reset; + } + + public ResolvableType getMockType() { + return this.mockType; + } + + public QualifierDefinition getQualifier() { + return qualifier; + } + + public Object getMockInstance() { + return mockInstance; + } + + public void resetMock() { + if (mockInstance != null) { + Mockito.reset(mockInstance); + } + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !getClass().isAssignableFrom(obj.getClass())) { + return false; + } + Definition other = (Definition) obj; + boolean result = true; + result = result && ObjectUtils.nullSafeEquals(this.mockType, other.mockType); + result = result && ObjectUtils.nullSafeEquals(this.name, other.name); + result = result && ObjectUtils.nullSafeEquals(this.type, other.type); + result = result && ObjectUtils.nullSafeEquals(this.module, other.module); + result = result && ObjectUtils.nullSafeEquals(this.field, other.field); + result = result && ObjectUtils.nullSafeEquals(this.reset, other.reset); + result = result && ObjectUtils.nullSafeEquals(this.qualifier, other.qualifier); + return result; + } + + @Override + public int hashCode() { + int result = 1; + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.mockType); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.name); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.type); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.module); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.field); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.reset); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.qualifier); + return result; + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/MockDefinition.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/MockDefinition.java new file mode 100644 index 000000000..3d7abdd26 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/MockDefinition.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.definition; + +import org.mockito.Answers; +import org.mockito.MockSettings; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.ResolvableType; +import org.springframework.core.style.ToStringCreator; +import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import static org.mockito.Mockito.mock; + +/** + * A complete definition that can be used to create a Mockito mock. + * + * @author pengym + * @version MockDefinition.java, v 0.1 2023年08月07日 19:42 pengym + */ +public class MockDefinition extends Definition { + + private static final int MULTIPLIER = 31; + + private final Set> extraInterfaces; + + private final Answers answer; + + private final boolean serializable; + + public MockDefinition(ResolvableType resolvableType, String name, ResolvableType type, + String module, String field, Class[] extraInterfaces, Answers answer, + boolean serializable, MockReset reset, QualifierDefinition qualifier) { + super(resolvableType, name, type, module, field, reset, qualifier); + this.extraInterfaces = asClassSet(extraInterfaces); + this.answer = (answer != null) ? answer : Answers.RETURNS_DEFAULTS; + this.serializable = serializable; + } + + public Set> getExtraInterfaces() { + return extraInterfaces; + } + + public Answers getAnswer() { + return answer; + } + + public boolean isSerializable() { + return serializable; + } + + @SuppressWarnings("unchecked") + public T createMock() { + if (mockInstance == null) { + MockSettings settings = MockReset.withSettings(getReset()); + if (!this.extraInterfaces.isEmpty()) { + settings.extraInterfaces(ClassUtils.toClassArray(this.extraInterfaces)); + } + settings.defaultAnswer(this.answer); + if (this.serializable) { + settings.serializable(); + } + mockInstance = (T) mock(this.getMockType().resolve(), settings); + } + return (T) mockInstance; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != getClass()) { + return false; + } + MockDefinition other = (MockDefinition) obj; + boolean result = super.equals(obj); + result = result && ObjectUtils.nullSafeEquals(this.extraInterfaces, other.extraInterfaces); + result = result && ObjectUtils.nullSafeEquals(this.answer, other.answer); + result = result && this.serializable == other.serializable; + return result; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.extraInterfaces); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.answer); + result = MULTIPLIER * result + Boolean.hashCode(this.serializable); + return result; + } + + @Override + public String toString() { + return new ToStringCreator(this).append("mockType", this.getMockType()) + .append("name", this.getName()).append("type", this.getType()) + .append("module", this.getModule()).append("field", this.getField()) + .append("extraInterfaces", this.extraInterfaces).append("answer", this.answer) + .append("serializable", this.serializable).append("reset", getReset()) + .append("qualifier", getQualifier()).toString(); + } + + private Set> asClassSet(Class[] classes) { + Set> classSet = new LinkedHashSet<>(); + if (classes != null) { + classSet.addAll(Arrays.asList(classes)); + } + return Collections.unmodifiableSet(classSet); + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinition.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinition.java new file mode 100644 index 000000000..fc895cafa --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinition.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.definition; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.annotation.MergedAnnotations; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; + +/** + * see org.springframework.boot.test.mock.mockito.QualifierDefinition + * + * @author huzijie + * @version QualifierDefinition.java, v 0.1 2023年08月15日 10:57 AM huzijie Exp $ + */ +public class QualifierDefinition { + + private final Field field; + + private final DependencyDescriptor descriptor; + + private final Set annotations; + + public QualifierDefinition(Field field, Set annotations) { + // We can't use the field or descriptor as part of the context key + // but we can assume that if two fields have the same qualifiers then + // it's safe for Spring to use either for qualifier logic + this.field = field; + this.descriptor = new DependencyDescriptor(field, true); + this.annotations = annotations; + } + + public boolean matches(ConfigurableListableBeanFactory beanFactory, String beanName) { + return beanFactory.isAutowireCandidate(beanName, this.descriptor); + } + + public void applyTo(RootBeanDefinition definition) { + definition.setQualifiedElement(this.field); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !getClass().isAssignableFrom(obj.getClass())) { + return false; + } + QualifierDefinition other = (QualifierDefinition) obj; + return this.annotations.equals(other.annotations); + } + + @Override + public int hashCode() { + return this.annotations.hashCode(); + } + + public static QualifierDefinition forElement(AnnotatedElement element) { + if (element != null && element instanceof Field) { + Field field = (Field) element; + Set annotations = getQualifierAnnotations(field); + if (!annotations.isEmpty()) { + return new QualifierDefinition(field, annotations); + } + } + return null; + } + + private static Set getQualifierAnnotations(Field field) { + // Assume that any annotations other than @MockBean/@SpyBean are qualifiers + Annotation[] candidates = field.getDeclaredAnnotations(); + Set annotations = new HashSet<>(candidates.length); + for (Annotation candidate : candidates) { + if (!isMockOrSpyAnnotation(candidate.annotationType())) { + annotations.add(candidate); + } + } + return annotations; + } + + private static boolean isMockOrSpyAnnotation(Class type) { + if (type.equals(MockBeanInjector.class) || type.equals(SpyBeanInjector.class)) { + return true; + } + MergedAnnotations metaAnnotations = MergedAnnotations.from(type); + return metaAnnotations.isPresent(MockBeanInjector.class) + || metaAnnotations.isPresent(SpyBeanInjector.class); + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinition.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinition.java new file mode 100644 index 000000000..ddf6fb32f --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinition.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.definition; + +import org.mockito.AdditionalAnswers; +import org.mockito.MockSettings; +import org.mockito.Mockito; +import org.mockito.listeners.VerificationStartedEvent; +import org.mockito.listeners.VerificationStartedListener; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.ResolvableType; +import org.springframework.core.style.ToStringCreator; +import org.springframework.test.util.AopTestUtils; +import org.springframework.util.Assert; + +import java.lang.reflect.Proxy; + +import static org.mockito.Mockito.mock; + +/** + * A complete definition that can be used to create a Mockito spy. + * + * @author pengym + * @version SpyDefinition.java, v 0.1 2023年08月07日 19:42 pengym + */ +public class SpyDefinition extends Definition { + + private static final int MULTIPLIER = 31; + + private final boolean proxyTargetAware; + + public SpyDefinition(ResolvableType resolvableType, String name, ResolvableType type, + String module, String field, MockReset reset, boolean proxyTargetAware, + QualifierDefinition qualifier) { + super(resolvableType, name, type, module, field, reset, qualifier); + this.proxyTargetAware = proxyTargetAware; + } + + @SuppressWarnings("unchecked") + public T createSpy(Object instance) { + if (mockInstance == null) { + Assert.notNull(instance, "Instance must not be null"); + Assert.isInstanceOf(this.getMockType().resolve(), instance); + if (Mockito.mockingDetails(instance).isSpy()) { + return (T) instance; + } + MockSettings settings = MockReset.withSettings(getReset()); + if (this.proxyTargetAware) { + settings + .verificationStartedListeners(new SpyDefinition.SpringAopBypassingVerificationStartedListener()); + } + Class toSpy; + if (Proxy.isProxyClass(instance.getClass())) { + settings.defaultAnswer(AdditionalAnswers.delegatesTo(instance)); + toSpy = this.getMockType().toClass(); + } else { + settings.defaultAnswer(Mockito.CALLS_REAL_METHODS); + settings.spiedInstance(instance); + toSpy = instance.getClass(); + } + mockInstance = mock(toSpy, settings); + } + return (T) mockInstance; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != getClass()) { + return false; + } + SpyDefinition other = (SpyDefinition) obj; + boolean result = super.equals(obj); + result = result && this.proxyTargetAware == other.proxyTargetAware; + return result; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = MULTIPLIER * result + Boolean.hashCode(this.proxyTargetAware); + return result; + } + + @Override + public String toString() { + return new ToStringCreator(this).append("mockType", this.getMockType()) + .append("name", this.getName()).append("type", this.getType()) + .append("module", this.getModule()).append("field", this.getField()) + .append("proxyTargetAware", this.proxyTargetAware).append("reset", getReset()) + .append("qualifier", getQualifier()).toString(); + } + + /** + * A {@link VerificationStartedListener} that bypasses any proxy created by Spring AOP + * when the verification of a spy starts. + */ + private static final class SpringAopBypassingVerificationStartedListener implements + VerificationStartedListener { + + @Override + public void onVerificationStarted(VerificationStartedEvent event) { + event.setMock(AopTestUtils.getUltimateTargetObject(event.getMock())); + } + + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParser.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParser.java new file mode 100644 index 000000000..7aae84c46 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParser.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.parser; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import com.alipay.sofa.test.mock.injector.definition.Definition; +import com.alipay.sofa.test.mock.injector.definition.MockDefinition; +import com.alipay.sofa.test.mock.injector.definition.QualifierDefinition; +import com.alipay.sofa.test.mock.injector.definition.SpyDefinition; +import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; +import org.springframework.test.context.TestContext; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * Parser for processing the {@link MockBeanInjector} and {@link SpyBeanInjector} annotations for the testClass + * + * @author pengym + * @version SofaBootTestAnnotationParser.java, v 0.1 2023年08月07日 17:52 pengym + */ +public class DefinitionParser { + + private final Set definitions; + + private final Map definitionFields; + + public DefinitionParser() { + this.definitions = new LinkedHashSet<>(); + this.definitionFields = new LinkedHashMap<>(); + } + + /** + * Parse the {@link MockBeanInjector} and {@link SpyBeanInjector} annotations for the testClass + * + * @param testClass The testClass, see {@link TestContext#getTestClass()} + */ + public void parse(Class testClass) { + Assert.notNull(testClass, "testClass must not be null"); + ReflectionUtils.doWithFields(testClass, field -> parseTestField(field, testClass)); + } + + private void parseTestField(Field testField, Class testClass) { + final MergedAnnotations mergedAnnotations = MergedAnnotations.from(testField, SearchStrategy.SUPERCLASS); + mergedAnnotations + .stream(MockBeanInjector.class) + .map(MergedAnnotation::synthesize) + .forEach(annotation -> parseSofaMockBeanAnnotation(annotation, testField, testClass)); + + mergedAnnotations + .stream(SpyBeanInjector.class) + .map(MergedAnnotation::synthesize) + .forEach(annotation -> parseSofaSpyBeanAnnotation(annotation, testField, testClass)); + } + + private void parseSofaMockBeanAnnotation(MockBeanInjector annotation, Field field, + Class testClass) { + ResolvableType typesToMock = deduceType(field, testClass); + MockDefinition mockDefinition = new MockDefinition(typesToMock, annotation.name(), + ResolvableType.forClass(annotation.value()), annotation.module(), annotation.field(), + annotation.extraInterfaces(), annotation.answer(), annotation.serializable(), + annotation.reset(), QualifierDefinition.forElement(field)); + registerDefinition(mockDefinition, field, "mock"); + } + + private void registerDefinition(Definition definition, Field field, String type) { + boolean isNewDefinition = this.definitions.add(definition); + Assert.state(isNewDefinition, () -> "Duplicate " + type + " definition " + definition); + this.definitionFields.put(definition, field); + } + + private void parseSofaSpyBeanAnnotation(SpyBeanInjector annotation, Field field, + Class testClass) { + ResolvableType typesToMock = deduceType(field, testClass); + SpyDefinition spyDefinition = new SpyDefinition(typesToMock, annotation.name(), + ResolvableType.forClass(annotation.value()), annotation.module(), annotation.field(), + annotation.reset(), annotation.proxyTargetAware(), + QualifierDefinition.forElement(field)); + registerDefinition(spyDefinition, field, "spy"); + } + + private ResolvableType deduceType(Field field, Class source) { + return (field.getGenericType() instanceof java.lang.reflect.TypeVariable) ? ResolvableType + .forField(field, source) : ResolvableType.forField(field); + } + + public Set getDefinitions() { + return Collections.unmodifiableSet(this.definitions); + } + + public Field getField(Definition definition) { + return this.definitionFields.get(definition); + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java new file mode 100644 index 000000000..6418fad25 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.resolver; + +import com.alipay.sofa.boot.constant.SofaBootConstants; +import com.alipay.sofa.isle.IsleDeploymentModel; +import com.alipay.sofa.test.mock.injector.definition.Definition; +import com.alipay.sofa.test.mock.injector.definition.QualifierDefinition; +import org.springframework.aop.framework.AopProxyUtils; +import org.springframework.aop.scope.ScopedProxyUtils; +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.core.ResolvableType; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * A resolve used to find inject target bean and create {@link BeanInjectorStub}. + * + * @author pengym + * @version BeanInjectorResolver.java, v 0.1 2023年08月07日 16:58 pengym + */ +public class BeanInjectorResolver { + + private static final String ISLE_MARKER_CLASS = "com.alipay.sofa.isle.ApplicationRuntimeModel"; + + private static final boolean ISLE_MODEL_EXIST = ClassUtils.isPresent( + ISLE_MARKER_CLASS, + null); + + private final ApplicationContext rootApplicationContext; + + private final Map isleApplicationContexts = new LinkedHashMap<>(); + + public BeanInjectorResolver(ApplicationContext applicationContext) { + this.rootApplicationContext = applicationContext; + if (ISLE_MODEL_EXIST) { + if (rootApplicationContext.containsBean(SofaBootConstants.APPLICATION)) { + IsleDeploymentModel isleDeploymentModel = applicationContext.getBean( + SofaBootConstants.APPLICATION, IsleDeploymentModel.class); + isleApplicationContexts + .putAll(isleDeploymentModel.getModuleApplicationContextMap()); + } + } + } + + public BeanInjectorStub resolveStub(Definition definition) { + // find target application context + ApplicationContext applicationContext = getApplicationContext(definition); + ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) applicationContext + .getAutowireCapableBeanFactory(); + + // find target beanName + String beanName = getBeanName(beanFactory, definition); + + // find target bean instance + if (!beanFactory.containsBean(beanName)) { + throw new IllegalStateException("Unable to create bean injector to bean [" + beanName + + "] target bean not exist"); + } + Object bean = resolveTargetObject(beanFactory.getBean(beanName)); + + // inject target bean field + return injectTargetBeanField(bean, beanName, definition); + } + + private ApplicationContext getApplicationContext(Definition definition) { + String module = definition.getModule(); + if (StringUtils.hasText(module)) { + ApplicationContext applicationContext = isleApplicationContexts.get(module); + if (applicationContext == null) { + throw new IllegalStateException("Unable to find target module [" + module + + "] when resolve injector: " + definition); + } + return applicationContext; + } else { + return rootApplicationContext; + } + } + + private BeanInjectorStub injectTargetBeanField(Object bean, String beanName, + Definition definition) { + String fieldName = definition.getField(); + Field targetField = ReflectionUtils.findField(bean.getClass(), fieldName); + + if (targetField == null) { + throw new IllegalStateException("Unable to inject target field to bean " + beanName + + ", can not find field " + fieldName + " in " + + bean.getClass()); + } + + BeanInjectorStub beanStubbedField = new BeanInjectorStub(definition, targetField, bean); + beanStubbedField.inject(); + return beanStubbedField; + } + + private Object resolveTargetObject(Object obj) { + if (!AopUtils.isAopProxy(obj) && !AopUtils.isJdkDynamicProxy(obj)) { + return obj; + } + + // AopProxy or JdkDynamicProxy + return AopProxyUtils.getSingletonTarget(obj); + } + + private String getBeanName(ConfigurableListableBeanFactory beanFactory, Definition definition) { + if (StringUtils.hasText(definition.getName())) { + return definition.getName(); + } + Set existingBeans = getExistingBeans(beanFactory, definition.getType(), + definition.getQualifier()); + if (existingBeans.isEmpty()) { + throw new IllegalStateException( + "Unable to create bean injector to bean by type [" + definition.getType() + + "] expected a single matching bean to injector but no bean found"); + } + if (existingBeans.size() == 1) { + return existingBeans.iterator().next(); + } + String primaryCandidate = determinePrimaryCandidate(beanFactory, existingBeans, + definition.getType()); + if (primaryCandidate != null) { + return primaryCandidate; + } + throw new IllegalStateException( + "Unable to create bean injector to bean by type [" + definition.getType() + + "] expected a single matching bean to injector but found " + existingBeans); + } + + private Set getExistingBeans(ConfigurableListableBeanFactory beanFactory, + ResolvableType type, QualifierDefinition qualifier) { + Set candidates = new TreeSet<>(); + for (String candidate : getExistingBeans(beanFactory, type)) { + if (qualifier == null || qualifier.matches(beanFactory, candidate)) { + candidates.add(candidate); + } + } + return candidates; + } + + private Set getExistingBeans(ConfigurableListableBeanFactory beanFactory, ResolvableType resolvableType) { + Set beans = new LinkedHashSet<>( + Arrays.asList(beanFactory.getBeanNamesForType(resolvableType, true, false))); + Class type = resolvableType.resolve(Object.class); + String typeName = type.getName(); + for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class, true, false)) { + beanName = BeanFactoryUtils.transformedBeanName(beanName); + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + Object attribute = beanDefinition.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE); + if (resolvableType.equals(attribute) || type.equals(attribute) || typeName.equals(attribute)) { + beans.add(beanName); + } + } + beans.removeIf(this::isScopedTarget); + return beans; + } + + private String determinePrimaryCandidate(ConfigurableListableBeanFactory beanFactory, + Collection candidateBeanNames, + ResolvableType type) { + String primaryBeanName = null; + for (String candidateBeanName : candidateBeanNames) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(candidateBeanName); + if (beanDefinition.isPrimary()) { + if (primaryBeanName != null) { + throw new NoUniqueBeanDefinitionException(type.resolve(), + candidateBeanNames.size(), + "more than one 'primary' bean found among candidates: " + + Collections.singletonList(candidateBeanNames)); + } + primaryBeanName = candidateBeanName; + } + } + return primaryBeanName; + } + + private boolean isScopedTarget(String beanName) { + try { + return ScopedProxyUtils.isScopedTarget(beanName); + } catch (Throwable ex) { + return false; + } + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.java new file mode 100644 index 000000000..8b2bcb6de --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.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.test.mock.injector.resolver; + +import com.alipay.sofa.test.mock.injector.definition.Definition; +import com.alipay.sofa.test.mock.injector.definition.MockDefinition; +import com.alipay.sofa.test.mock.injector.definition.SpyDefinition; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; + +/** + * A bean injector stub which could transform target field value. + * + * @author huzijie + * @version BeanStubbedField.java, v 0.1 2023年08月17日 7:31 PM huzijie Exp $ + */ +public class BeanInjectorStub { + + /** + * Mock/Spy Definition + */ + private final Definition definition; + + /** + * Field to inject + */ + private final Field field; + + /** + * The original value of the injected field + */ + private final Object originalValue; + + /** + * The bean to inject field + */ + private final Object bean; + + public BeanInjectorStub(Definition definition, Field field, Object bean) { + this.definition = definition; + this.field = field; + this.bean = bean; + ReflectionUtils.makeAccessible(field); + this.originalValue = ReflectionUtils.getField(field, bean); + if (definition instanceof SpyDefinition && this.originalValue == null) { + throw new IllegalStateException("Unable to create spy to inject target field " + field + + " when origin value is null"); + } + } + + /** + * Inject the mock/spy to target field. + */ + public void inject() { + if (definition instanceof MockDefinition) { + ReflectionUtils.setField(field, bean, ((MockDefinition) definition).createMock()); + } else if (definition instanceof SpyDefinition) { + ReflectionUtils.setField(field, bean, + ((SpyDefinition) definition).createSpy(originalValue)); + } + } + + /** + * Reset the target field value. + */ + public void reset() { + ReflectionUtils.setField(field, bean, originalValue); + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/resources/META-INF/spring.factories b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..41b6565df --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +# Add TestExecutionListener +org.springframework.test.context.TestExecutionListener=com.alipay.sofa.test.mock.injector.InjectorMockTestExecutionListener diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockApplicationContextCacheTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockApplicationContextCacheTests.java new file mode 100644 index 000000000..31e89d319 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockApplicationContextCacheTests.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import org.junit.After; +import org.junit.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.BootstrapContext; +import org.springframework.test.context.MergedContextConfiguration; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate; +import org.springframework.test.context.cache.DefaultContextCache; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Ensure mock not dirty application context cache. + * + * @author huzijie + * @version InjectorMockApplicationContextCacheTests.java, v 0.1 2023年08月21日 7:32 PM huzijie Exp $ + */ +public class InjectorMockApplicationContextCacheTests { + + private final DefaultContextCache contextCache = new DefaultContextCache(); + + private final DefaultCacheAwareContextLoaderDelegate delegate = new DefaultCacheAwareContextLoaderDelegate( + this.contextCache); + + @After + @SuppressWarnings("unchecked") + public void clearCache() { + Map contexts = (Map) ReflectionTestUtils + .getField(this.contextCache, "contextMap"); + for (ApplicationContext context : contexts.values()) { + if (context instanceof ConfigurableApplicationContext) { + ((ConfigurableApplicationContext) context).close(); + } + } + this.contextCache.clear(); + } + + @Test + public void useCacheWhenResolveInjectorMockBeanAnnotation() { + bootstrapContext(TestClass.class); + assertThat(this.contextCache.size()).isOne(); + bootstrapContext(MockedBeanTestClass.class); + assertThat(this.contextCache.size()).isOne(); + } + + @SuppressWarnings("rawtypes") + private void bootstrapContext(Class testClass) { + SpringBootTestContextBootstrapper bootstrapper = new SpringBootTestContextBootstrapper(); + BootstrapContext bootstrapContext = mock(BootstrapContext.class); + given((Class) bootstrapContext.getTestClass()).willReturn(testClass); + bootstrapper.setBootstrapContext(bootstrapContext); + given(bootstrapContext.getCacheAwareContextLoaderDelegate()).willReturn(this.delegate); + TestContext testContext = bootstrapper.buildTestContext(); + testContext.getApplicationContext(); + } + + @SpringBootTest(classes = TestConfiguration.class, properties = "spring.application.name=test") + static class TestClass { + + } + + @SpringBootTest(classes = TestConfiguration.class, properties = "spring.application.name=test") + static class MockedBeanTestClass { + + @MockBeanInjector(field = "testBean", type = InjectBean.class) + private TestBean testBean; + + } + + @Configuration + static class TestConfiguration { + + @Bean + TestBean testBean() { + return new TestBean(); + } + + @Bean + InjectBean injectBean() { + return new InjectBean(); + } + + } + + static class TestBean { + + } + + static class InjectBean { + + private TestBean testBean; + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListenerTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListenerTests.java new file mode 100644 index 000000000..3b53e87cf --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListenerTests.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.definition.MockDefinition; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.resolver.BeanInjectorStub; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.ResolvableType; +import org.springframework.test.context.TestContext; + +import java.util.HashSet; +import java.util.Set; + +import static com.alipay.sofa.test.mock.injector.InjectorMockTestExecutionListener.STUBBED_DEFINITIONS; +import static com.alipay.sofa.test.mock.injector.InjectorMockTestExecutionListener.STUBBED_FIELDS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; + +/** + * Tests for {@link InjectorMockTestExecutionListener}. + * + * @author huzijie + * @version InjectorMockTestExecutionListenerTests.java, v 0.1 2023年08月21日 4:41 PM huzijie Exp $ + */ +public class InjectorMockTestExecutionListenerTests { + + private final InjectorMockTestExecutionListener listener = new InjectorMockTestExecutionListener(); + + @Test + public void prepareTestInstanceShouldInjectMockBean() { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext( + TargetBean.class); + + WithMockBean instance = new WithMockBean(); + TestContext testContext = mockTestContext(instance); + given(testContext.getApplicationContext()).willReturn(applicationContext); + this.listener.prepareTestInstance(testContext); + ExampleService mock = instance.getMockBean(); + assertThat(mock).isNotNull(); + assertThat(Mockito.mockingDetails(mock).isMock()).isTrue(); + + TargetBean targetBean = applicationContext.getBean(TargetBean.class); + ExampleService injectField = targetBean.getFieldA(); + assertThat(mock).isEqualTo(injectField); + + verify(testContext, times(2)).setAttribute(anyString(), any()); + } + + @Test + public void prepareTestInstanceWhenInjectTargetAlreadyExist() { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TargetBean.class); + + WithMockBean instance = new WithMockBean(); + TestContext testContext = mockTestContext(instance); + given(testContext.getApplicationContext()).willReturn(applicationContext); + this.listener.prepareTestInstance(testContext); + assertThatIllegalStateException().isThrownBy(() -> this.listener.prepareTestInstance(testContext)) + .withMessageContaining("The existing value"); + } + + @Test + public void beforeTestMethodShouldRestMock() { + WithMockBean instance = new WithMockBean(); + TestContext testContext = mockTestContext(instance); + + MockDefinition definition = createMockDefinition(ExampleService.class, MockReset.BEFORE); + ExampleService mock = definition.createMock(); + when(mock.greeting()).thenReturn("abc"); + assertThat(mock.greeting()).isEqualTo("abc"); + + Set result = new HashSet(); + result.add(definition); + given(testContext.getAttribute(eq(STUBBED_DEFINITIONS))).willReturn(result); + this.listener.beforeTestMethod(testContext); + + assertThat(mock.greeting()).isEqualTo(null); + } + + @Test + public void afterTestMethodShouldRestMock() { + WithMockBean instance = new WithMockBean(); + TestContext testContext = mockTestContext(instance); + + MockDefinition definition = createMockDefinition(ExampleService.class, MockReset.AFTER); + ExampleService mock = definition.createMock(); + when(mock.greeting()).thenReturn("abc"); + assertThat(mock.greeting()).isEqualTo("abc"); + + Set result = new HashSet(); + result.add(definition); + given(testContext.getAttribute(eq(STUBBED_DEFINITIONS))).willReturn(result); + this.listener.afterTestMethod(testContext); + + assertThat(mock.greeting()).isEqualTo(null); + } + + @Test + public void afterTestClassShouldRestInjectStubs() { + WithMockBean instance = new WithMockBean(); + TestContext testContext = mockTestContext(instance); + + BeanInjectorStub beanInjectorStub = mock(BeanInjectorStub.class); + + Set result = new HashSet(); + result.add(beanInjectorStub); + given(testContext.getAttribute(eq(STUBBED_FIELDS))).willReturn(result); + this.listener.afterTestMethod(testContext); + + verify(beanInjectorStub, only()).reset(); + } + + private MockDefinition createMockDefinition(Class clazz, MockReset mockReset) { + return new MockDefinition(ResolvableType.forClass(clazz), null, null, null, null, null, + null, false, mockReset, null); + } + + private TestContext mockTestContext(Object instance) { + TestContext testContext = mock(TestContext.class); + given(testContext.getTestInstance()).willReturn(instance); + given(testContext.getTestClass()).willReturn((Class) instance.getClass()); + return testContext; + } + + @Configuration + static class TargetBean { + + private ExampleService fieldA; + + public ExampleService getFieldA() { + return fieldA; + } + } + + static class WithMockBean { + + public ExampleService getMockBean() { + return mockBean; + } + + @MockBeanInjector(field = "fieldA", type = TargetBean.class) + private ExampleService mockBean; + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/MockDefinitionTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/MockDefinitionTests.java new file mode 100644 index 000000000..15388be94 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/MockDefinitionTests.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.definition; + +import com.alipay.sofa.test.mock.injector.example.ExampleExtraInterface; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import org.junit.Test; +import org.mockito.Answers; +import org.mockito.Mockito; +import org.mockito.mock.MockCreationSettings; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.ResolvableType; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link MockDefinition}. + * + * @author huzijie + * @version MockDefinitionTests.java, v 0.1 2023年08月21日 3:30 PM huzijie Exp $ + */ +public class MockDefinitionTests { + + private static final ResolvableType EXAMPLE_SERVICE_TYPE = ResolvableType + .forClass(ExampleService.class); + + @Test + public void classToMockMustNotBeNull() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new MockDefinition(null,null,null, null, null, + null, null, false, null, null)) + .withMessageContaining("MockType must not be null"); + } + + @Test + public void createWithDefaults() { + MockDefinition definition = new MockDefinition(EXAMPLE_SERVICE_TYPE, null, null, null, + "Field", null, null, false, null, null); + assertThat(definition.getName()).isNull(); + assertThat(definition.getModule()).isNull(); + assertThat(definition.getField()).isEqualTo("Field"); + assertThat(definition.getMockType()).isEqualTo(EXAMPLE_SERVICE_TYPE); + assertThat(definition.getType()).isEqualTo(null); + assertThat(definition.getExtraInterfaces()).isEmpty(); + assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS); + assertThat(definition.isSerializable()).isFalse(); + assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); + assertThat(definition.getQualifier()).isNull(); + } + + @Test + public void createExplicit() { + QualifierDefinition qualifier = mock(QualifierDefinition.class); + MockDefinition definition = new MockDefinition(EXAMPLE_SERVICE_TYPE, "name", + EXAMPLE_SERVICE_TYPE, "Module", "Field", + new Class[] { ExampleExtraInterface.class }, Answers.RETURNS_SMART_NULLS, true, + MockReset.BEFORE, qualifier); + assertThat(definition.getName()).isEqualTo("name"); + assertThat(definition.getModule()).isEqualTo("Module"); + assertThat(definition.getField()).isEqualTo("Field"); + assertThat(definition.getType()).isEqualTo(EXAMPLE_SERVICE_TYPE); + assertThat(definition.getMockType()).isEqualTo(EXAMPLE_SERVICE_TYPE); + assertThat(definition.getExtraInterfaces()).containsExactly(ExampleExtraInterface.class); + assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); + assertThat(definition.isSerializable()).isTrue(); + assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); + assertThat(definition.getQualifier()).isEqualTo(qualifier); + } + + @Test + public void createMock() { + MockDefinition definition = new MockDefinition(EXAMPLE_SERVICE_TYPE, "name", + EXAMPLE_SERVICE_TYPE, "Module", "Field", + new Class[] { ExampleExtraInterface.class }, Answers.RETURNS_SMART_NULLS, true, + MockReset.BEFORE, null); + ExampleService mock = definition.createMock(); + MockCreationSettings settings = Mockito.mockingDetails(mock).getMockCreationSettings(); + assertThat(mock).isEqualTo(definition.getMockInstance()); + assertThat(mock).isInstanceOf(ExampleService.class); + assertThat(mock).isInstanceOf(ExampleExtraInterface.class); + assertThat(settings.getDefaultAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); + assertThat(settings.isSerializable()).isTrue(); + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinitionTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinitionTests.java new file mode 100644 index 000000000..2819d91df --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/QualifierDefinitionTests.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.definition; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ReflectionUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.then; + +/** + * Tests for {@link QualifierDefinition}. + * + * @author huzijie + * @version QualifierDefinitionTests.java, v 0.1 2023年08月21日 3:30 PM huzijie Exp $ + */ +@RunWith(MockitoJUnitRunner.class) +public class QualifierDefinitionTests { + + @Mock + private ConfigurableListableBeanFactory beanFactory; + + @Test + public void forElementFieldIsNullShouldReturnNull() { + assertThat(QualifierDefinition.forElement((Field) null)).isNull(); + } + + @Test + public void forElementWhenElementIsNotFieldShouldReturnNull() { + assertThat(QualifierDefinition.forElement(getClass())).isNull(); + } + + @Test + public void forElementWhenElementIsFieldWithNoQualifiersShouldReturnNull() { + QualifierDefinition definition = QualifierDefinition.forElement(ReflectionUtils.findField( + ConfigA.class, "noQualifier")); + assertThat(definition).isNull(); + } + + @Test + public void forElementWhenElementIsFieldWithQualifierShouldReturnDefinition() { + QualifierDefinition definition = QualifierDefinition.forElement(ReflectionUtils.findField( + ConfigA.class, "directQualifier")); + assertThat(definition).isNotNull(); + } + + @Test + public void matchesShouldCallBeanFactory() { + Field field = ReflectionUtils.findField(ConfigA.class, "directQualifier"); + QualifierDefinition qualifierDefinition = QualifierDefinition.forElement(field); + qualifierDefinition.matches(this.beanFactory, "bean"); + then(this.beanFactory).should() + .isAutowireCandidate(eq("bean"), argThat( + (dependencyDescriptor) -> { + assertThat(dependencyDescriptor.getAnnotatedElement()).isEqualTo(field); + return true; + })); + } + + @Test + public void applyToShouldSetQualifierElement() { + Field field = ReflectionUtils.findField(ConfigA.class, "directQualifier"); + QualifierDefinition qualifierDefinition = QualifierDefinition.forElement(field); + RootBeanDefinition definition = new RootBeanDefinition(); + qualifierDefinition.applyTo(definition); + assertThat(definition.getQualifiedElement()).isEqualTo(field); + } + + @Test + public void hashCodeAndEqualsShouldWorkOnDifferentClasses() { + QualifierDefinition directQualifier1 = QualifierDefinition.forElement(ReflectionUtils + .findField(ConfigA.class, "directQualifier")); + QualifierDefinition directQualifier2 = QualifierDefinition.forElement(ReflectionUtils + .findField(ConfigB.class, "directQualifier")); + QualifierDefinition differentDirectQualifier1 = QualifierDefinition + .forElement(ReflectionUtils.findField(ConfigA.class, "differentDirectQualifier")); + QualifierDefinition differentDirectQualifier2 = QualifierDefinition + .forElement(ReflectionUtils.findField(ConfigB.class, "differentDirectQualifier")); + QualifierDefinition customQualifier1 = QualifierDefinition.forElement(ReflectionUtils + .findField(ConfigA.class, "customQualifier")); + QualifierDefinition customQualifier2 = QualifierDefinition.forElement(ReflectionUtils + .findField(ConfigB.class, "customQualifier")); + assertThat(directQualifier1).hasSameHashCodeAs(directQualifier2); + assertThat(differentDirectQualifier1).hasSameHashCodeAs(differentDirectQualifier2); + assertThat(customQualifier1).hasSameHashCodeAs(customQualifier2); + assertThat(differentDirectQualifier1).isEqualTo(differentDirectQualifier1) + .isEqualTo(differentDirectQualifier2).isNotEqualTo(directQualifier2); + assertThat(directQualifier1).isEqualTo(directQualifier1).isEqualTo(directQualifier2) + .isNotEqualTo(differentDirectQualifier1); + assertThat(customQualifier1).isEqualTo(customQualifier1).isEqualTo(customQualifier2) + .isNotEqualTo(differentDirectQualifier1); + } + + @Configuration(proxyBeanMethods = false) + static class ConfigA { + + @MockBeanInjector(field = "Field") + private Object noQualifier; + + @MockBeanInjector(field = "Field") + @Qualifier("test") + private Object directQualifier; + + @MockBeanInjector(field = "Field") + @Qualifier("different") + private Object differentDirectQualifier; + + @MockBeanInjector(field = "Field") + @CustomQualifier + private Object customQualifier; + + } + + static class ConfigB { + + @MockBeanInjector(field = "Field") + @Qualifier("test") + private Object directQualifier; + + @MockBeanInjector(field = "Field") + @Qualifier("different") + private Object differentDirectQualifier; + + @MockBeanInjector(field = "Field") + @CustomQualifier + private Object customQualifier; + + } + + @Qualifier + @Retention(RetentionPolicy.RUNTIME) + public @interface CustomQualifier { + + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinitionTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinitionTests.java new file mode 100644 index 000000000..15fd53773 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/definition/SpyDefinitionTests.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.definition; + +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import com.alipay.sofa.test.mock.injector.example.RealExampleService; +import org.junit.Test; +import org.mockito.Answers; +import org.mockito.Mockito; +import org.mockito.mock.MockCreationSettings; +import org.springframework.boot.test.mock.mockito.MockReset; +import org.springframework.core.ResolvableType; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link SpyDefinition}. + * + * @author huzijie + * @version SpyDefinitionTests.java, v 0.1 2023年08月21日 3:31 PM huzijie Exp $ + */ +public class SpyDefinitionTests { + + private static final ResolvableType REAL_SERVICE_TYPE = ResolvableType + .forClass(ExampleService.class); + + @Test + public void classToSpyMustNotBeNull() { + assertThatIllegalArgumentException().isThrownBy(() -> new SpyDefinition(null, null, null, + null, null,null, true, null)) + .withMessageContaining("MockType must not be null"); + } + + @Test + public void createWithDefaults() { + SpyDefinition definition = new SpyDefinition(REAL_SERVICE_TYPE, null, null, null, null, + null, true, null); + assertThat(definition.getName()).isNull(); + assertThat(definition.getType()).isNull(); + assertThat(definition.getModule()).isNull(); + assertThat(definition.getField()).isNull(); + assertThat(definition.getMockType()).isEqualTo(REAL_SERVICE_TYPE); + assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); + assertThat(definition.getQualifier()).isNull(); + } + + @Test + public void createExplicit() { + QualifierDefinition qualifier = mock(QualifierDefinition.class); + SpyDefinition definition = new SpyDefinition(REAL_SERVICE_TYPE, "name", REAL_SERVICE_TYPE, + "Module", "Field", MockReset.BEFORE, false, qualifier); + assertThat(definition.getName()).isEqualTo("name"); + assertThat(definition.getType()).isEqualTo(REAL_SERVICE_TYPE); + assertThat(definition.getModule()).isEqualTo("Module"); + assertThat(definition.getField()).isEqualTo("Field"); + assertThat(definition.getMockType()).isEqualTo(REAL_SERVICE_TYPE); + assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); + assertThat(definition.getQualifier()).isEqualTo(qualifier); + } + + @Test + public void createSpy() { + SpyDefinition definition = new SpyDefinition(REAL_SERVICE_TYPE, "name", REAL_SERVICE_TYPE, + "Module", "Field", MockReset.BEFORE, false, null); + RealExampleService spy = definition.createSpy(new RealExampleService("hello")); + MockCreationSettings settings = Mockito.mockingDetails(spy).getMockCreationSettings(); + assertThat(spy).isInstanceOf(ExampleService.class); + assertThat(spy).isEqualTo(definition.getMockInstance()); + assertThat(settings.getDefaultAnswer()).isEqualTo(Answers.CALLS_REAL_METHODS); + } + + @Test + public void createSpyWhenNullInstanceShouldThrowException() { + SpyDefinition definition = new SpyDefinition(REAL_SERVICE_TYPE, + "name", REAL_SERVICE_TYPE, "Module", "Field", MockReset.BEFORE, false, null); + assertThatIllegalArgumentException().isThrownBy(() -> definition.createSpy(null)) + .withMessageContaining("Instance must not be null"); + } + + @Test + public void createSpyWhenWrongInstanceShouldThrowException() { + SpyDefinition definition = new SpyDefinition(REAL_SERVICE_TYPE, + "name", REAL_SERVICE_TYPE, "Module", "Field", MockReset.BEFORE, false, null); + assertThatIllegalArgumentException().isThrownBy(() -> definition.createSpy(new ExampleServiceCaller())) + .withMessageContaining("must be an instance of"); + } + + @Test + public void createSpyTwice() { + SpyDefinition definition = new SpyDefinition(REAL_SERVICE_TYPE, "name", REAL_SERVICE_TYPE, + "Module", "Field", MockReset.BEFORE, false, null); + Object instance = new RealExampleService("hello"); + instance = definition.createSpy(instance); + assertThat(instance).isEqualTo(definition.createSpy(instance)); + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleExtraInterface.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleExtraInterface.java new file mode 100644 index 000000000..8c279a2ec --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleExtraInterface.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.test.mock.injector.example; + +/** + * @author huzijie + * @version ExampleExtraInterface.java, v 0.1 2023年08月21日 3:18 PM huzijie Exp $ + */ +public interface ExampleExtraInterface { + + void doExtra(); +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleService.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleService.java new file mode 100644 index 000000000..f31b93e4f --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleService.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.example; + +/** + * @author huzijie + * @version ExampleService.java, v 0.1 2023年08月21日 3:14 PM huzijie Exp $ + */ +public interface ExampleService { + + String greeting(); + + String hello(); +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCaller.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCaller.java new file mode 100644 index 000000000..c949a7899 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCaller.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.test.mock.injector.example; + +/** + * @author huzijie + * @version ExampleServiceCaller.java, v 0.1 2023年08月21日 3:16 PM huzijie Exp $ + */ +public class ExampleServiceCaller implements ExampleServiceCallerInterface { + + private ExampleService service; + + public ExampleService getService() { + return this.service; + } + + public void setService(ExampleService service) { + this.service = service; + } + + @Override + public String sayGreeting() { + return this.service.greeting(); + } + + @Override + public String sayHello() { + return service.hello(); + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCallerInterface.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCallerInterface.java new file mode 100644 index 000000000..d3c572715 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/ExampleServiceCallerInterface.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.test.mock.injector.example; + +/** + * @author huzijie + * @version ExampleServiceCallerInterface.java, v 0.1 2023年08月21日 3:16 PM huzijie Exp $ + */ +public interface ExampleServiceCallerInterface { + + String sayGreeting(); + + String sayHello(); + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/RealExampleService.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/RealExampleService.java new file mode 100644 index 000000000..7dad35632 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/example/RealExampleService.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.example; + +/** + * @author huzijie + * @version RealExampleService.java, v 0.1 2023年08月21日 3:16 PM huzijie Exp $ + */ +public class RealExampleService implements ExampleService { + + private final String greeting; + + public RealExampleService(String greeting) { + this.greeting = greeting; + } + + @Override + public String greeting() { + return this.greeting; + } + + @Override + public String hello() { + return "hello"; + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToAopProxyBeanTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToAopProxyBeanTests.java new file mode 100644 index 000000000..e4d3b2ca3 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToAopProxyBeanTests.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCallerInterface; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator; +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.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link MockBeanInjector} with aop proxy bean. + * + * @author huzijie + * @version InjectMockToNormalBeanTests.java, v 0.1 2023年08月21日 7:53 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectMockToAopProxyBeanTests.Config.class) +public class InjectMockToAopProxyBeanTests { + + @MockBeanInjector(field = "service", type = ExampleServiceCallerInterface.class) + private ExampleService exampleService; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkMock() { + when(exampleService.greeting()).thenReturn("amock"); + ExampleServiceCallerInterface bean = this.applicationContext + .getBean(ExampleServiceCallerInterface.class); + assertThat(bean.sayGreeting()).isEqualTo("amock"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + ExampleServiceCallerInterface exampleServiceCaller() { + return new ExampleServiceCaller(); + } + + @Bean + BeanNameAutoProxyCreator beanNameAutoProxyCreator() { + BeanNameAutoProxyCreator autoProxyCreator = new BeanNameAutoProxyCreator(); + autoProxyCreator.setBeanNames("exampleServiceCaller"); + return autoProxyCreator; + } + + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToFactoryBeanTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToFactoryBeanTests.java new file mode 100644 index 000000000..131638965 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToFactoryBeanTests.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.FactoryBean; +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.test.context.junit4.SpringRunner; + +import static org.mockito.Mockito.when; + +/** + * Tests for {@link MockBeanInjector} with factory bean. + * + * @author huzijie + * @version InjectMockToFactoryBeanTests.java, v 0.1 2023年08月21日 7:53 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectMockToFactoryBeanTests.Config.class) +public class InjectMockToFactoryBeanTests { + + @MockBeanInjector(field = "service", type = ExampleServiceCaller.class) + private ExampleService exampleService; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkMock() { + when(exampleService.greeting()).thenReturn("amock"); + ExampleServiceCaller bean = this.applicationContext.getBean(ExampleServiceCaller.class); + Assertions.assertThat(bean.sayGreeting()).isEqualTo("amock"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestFactoryBean testFactoryBean() { + return new TestFactoryBean(); + } + + } + + static class TestFactoryBean implements FactoryBean { + + private ExampleServiceCaller exampleServiceCaller = new ExampleServiceCaller(); + + @Override + public ExampleServiceCaller getObject() { + return exampleServiceCaller; + } + + @Override + public Class getObjectType() { + return ExampleServiceCaller.class; + } + + @Override + public boolean isSingleton() { + return false; + } + + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanExtensionTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanExtensionTests.java new file mode 100644 index 000000000..824cb2688 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanExtensionTests.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.integration; + +/** + * Concrete implementation of {@link InjectMockToGenericBeanTestBase}. + * + * @author huzijie + * @version InjectMockToGenericBeanExtensionTests.java, v 0.1 2023年08月21日 8:19 PM huzijie Exp $ + */ +public class InjectMockToGenericBeanExtensionTests + extends + InjectMockToGenericBeanTestBase { +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanTestBase.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanTestBase.java new file mode 100644 index 000000000..018907ed3 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToGenericBeanTestBase.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.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MockBeanInjector} with generic bean. + * + * @author huzijie + * @version InjectMockToGenericBeanTestBase.java, v 0.1 2023年08月21日 7:53 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectMockToGenericBeanTestBase.Config.class) +abstract class InjectMockToGenericBeanTestBase, U extends InjectMockToGenericBeanTestBase.Something> { + + @MockBeanInjector(field = "something", name = "thing") + private U something; + + @Autowired + private ApplicationContext applicationContext; + + @Test + @SuppressWarnings("unchecked") + public void checkMock() { + T bean = (T) this.applicationContext.getBean(Thing.class); + assertThat(bean.getSomething()).isEqualTo(something); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + ThingImpl thing() { + return new ThingImpl(); + } + + } + + abstract static class Thing { + + protected T something; + + T getSomething() { + return this.something; + } + + void setSomething(T something) { + this.something = something; + } + + } + + static class SomethingImpl extends Something { + + } + + static class ThingImpl extends Thing { + + } + + static class Something { + + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToNormalBeanTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToNormalBeanTests.java new file mode 100644 index 000000000..f8c6c9f6c --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectMockToNormalBeanTests.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.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.mockito.Mockito.when; + +/** + * Tests for {@link MockBeanInjector} with normal bean. + * + * @author huzijie + * @version InjectMockToNormalBeanTests.java, v 0.1 2023年08月21日 7:53 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectMockToNormalBeanTests.Config.class) +public class InjectMockToNormalBeanTests { + + @MockBeanInjector(field = "service", type = ExampleServiceCaller.class) + private ExampleService exampleService; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkMock() { + when(exampleService.greeting()).thenReturn("amock"); + ExampleServiceCaller bean = this.applicationContext.getBean(ExampleServiceCaller.class); + Assertions.assertThat(bean.sayGreeting()).isEqualTo("amock"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + ExampleServiceCaller exampleServiceCaller() { + return new ExampleServiceCaller(); + } + + } + +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToAopProxyBeanTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToAopProxyBeanTests.java new file mode 100644 index 000000000..90458773a --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToAopProxyBeanTests.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCallerInterface; +import com.alipay.sofa.test.mock.injector.example.RealExampleService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator; +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.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link SpyBeanInjector} with aop proxy bean. + * + * @author huzijie + * @version InjectSpyToAopProxyBeanTests.java, v 0.1 2023年08月21日 8:24 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectSpyToAopProxyBeanTests.Config.class) +public class InjectSpyToAopProxyBeanTests { + + @SpyBeanInjector(field = "service", type = ExampleServiceCaller.class) + private ExampleService exampleService; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkSpy() { + when(exampleService.greeting()).thenReturn("aspy"); + ExampleServiceCallerInterface bean = this.applicationContext + .getBean(ExampleServiceCallerInterface.class); + assertThat(bean.sayGreeting()).isEqualTo("aspy"); + assertThat(bean.sayHello()).isEqualTo("hello"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + ExampleServiceCallerInterface exampleServiceCaller() { + ExampleServiceCaller exampleServiceCaller = new ExampleServiceCaller(); + exampleServiceCaller.setService(new RealExampleService("greeting")); + return exampleServiceCaller; + } + + @Bean + BeanNameAutoProxyCreator beanNameAutoProxyCreator() { + BeanNameAutoProxyCreator autoProxyCreator = new BeanNameAutoProxyCreator(); + autoProxyCreator.setBeanNames("exampleServiceCaller"); + autoProxyCreator.setProxyTargetClass(true); + return autoProxyCreator; + } + + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToFactoryBeanTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToFactoryBeanTests.java new file mode 100644 index 000000000..a25c423e9 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToFactoryBeanTests.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import com.alipay.sofa.test.mock.injector.example.RealExampleService; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.FactoryBean; +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.test.context.junit4.SpringRunner; + +import static org.mockito.Mockito.when; + +/** + * Tests for {@link SpyBeanInjector} with factory bean. + * + * @author huzijie + * @version InjectSpyToFactoryBeanTests.java, v 0.1 2023年08月21日 8:24 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectSpyToFactoryBeanTests.Config.class) +public class InjectSpyToFactoryBeanTests { + + @SpyBeanInjector(field = "service", type = ExampleServiceCaller.class) + private ExampleService exampleService; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkSpy() { + when(exampleService.greeting()).thenReturn("aspy"); + ExampleServiceCaller bean = this.applicationContext.getBean(ExampleServiceCaller.class); + Assertions.assertThat(bean.sayGreeting()).isEqualTo("aspy"); + Assertions.assertThat(bean.sayHello()).isEqualTo("hello"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestFactoryBean testFactoryBean() { + return new TestFactoryBean(); + } + + } + + static class TestFactoryBean implements FactoryBean { + + private final ExampleServiceCaller exampleServiceCaller; + + public TestFactoryBean() { + exampleServiceCaller = new ExampleServiceCaller(); + exampleServiceCaller.setService(new RealExampleService("greeting")); + } + + @Override + public ExampleServiceCaller getObject() { + return exampleServiceCaller; + } + + @Override + public Class getObjectType() { + return ExampleServiceCaller.class; + } + + @Override + public boolean isSingleton() { + return false; + } + + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToNormalBeanTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToNormalBeanTests.java new file mode 100644 index 000000000..95dc97b61 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyToNormalBeanTests.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import com.alipay.sofa.test.mock.injector.example.RealExampleService; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.mockito.Mockito.when; + +/** + * Tests for {@link SpyBeanInjector} with normal bean. + * + * @author huzijie + * @version InjectSpyToNormalBeanTests.java, v 0.1 2023年08月21日 8:24 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectSpyToNormalBeanTests.Config.class) +public class InjectSpyToNormalBeanTests { + + @SpyBeanInjector(field = "service", type = ExampleServiceCaller.class) + private ExampleService exampleService; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkSpy() { + when(exampleService.greeting()).thenReturn("aspy"); + ExampleServiceCaller bean = this.applicationContext.getBean(ExampleServiceCaller.class); + Assertions.assertThat(bean.sayGreeting()).isEqualTo("aspy"); + Assertions.assertThat(bean.sayHello()).isEqualTo("hello"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + ExampleServiceCaller exampleServiceCaller() { + ExampleServiceCaller exampleServiceCaller = new ExampleServiceCaller(); + exampleServiceCaller.setService(new RealExampleService("greeting")); + return exampleServiceCaller; + } + + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyWithJdkProxyTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyWithJdkProxyTests.java new file mode 100644 index 000000000..131deda57 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/InjectSpyWithJdkProxyTests.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.integration; + +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.ExampleServiceCaller; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.lang.reflect.Proxy; + +import static org.mockito.Mockito.when; + +/** + * Tests for {@link SpyBeanInjector} with a JDK proxy. + * + * @author huzijie + * @version InjectSpyWithJdkProxyTests.java, v 0.1 2023年08月21日 8:35 PM huzijie Exp $ + */ +@SpringBootTest(classes = TestSofaBootApplication.class) +@RunWith(SpringRunner.class) +@Import(InjectSpyWithJdkProxyTests.Config.class) +public class InjectSpyWithJdkProxyTests { + + @SpyBeanInjector(field = "service", type = ExampleServiceCaller.class) + private ExampleService exampleService; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void checkSpy() { + when(exampleService.greeting()).thenReturn("aspy"); + ExampleServiceCaller bean = this.applicationContext.getBean(ExampleServiceCaller.class); + Assertions.assertThat(bean.sayGreeting()).isEqualTo("aspy"); + Assertions.assertThat(bean.sayHello()).isEqualTo("jdkProxy"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + ExampleServiceCaller exampleServiceCaller() { + ExampleServiceCaller exampleServiceCaller = new ExampleServiceCaller(); + exampleServiceCaller.setService(exampleService()); + return exampleServiceCaller; + } + + private ExampleService exampleService() { + return (ExampleService) Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[] { ExampleService.class }, (proxy, method, args) -> "jdkProxy"); + } + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/TestSofaBootApplication.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/TestSofaBootApplication.java new file mode 100644 index 000000000..883f80bc7 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/integration/TestSofaBootApplication.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.test.mock.injector.integration; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author huzijie + * @version TestSofaBootApplication.java, v 0.1 2023年08月17日 8:46 PM huzijie Exp $ + */ +@SpringBootApplication +public class TestSofaBootApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(); + application.run(args); + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParserTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParserTests.java new file mode 100644 index 000000000..1bc410317 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/parser/DefinitionParserTests.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.parser; + +import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; +import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; +import com.alipay.sofa.test.mock.injector.definition.Definition; +import com.alipay.sofa.test.mock.injector.definition.MockDefinition; +import com.alipay.sofa.test.mock.injector.definition.SpyDefinition; +import com.alipay.sofa.test.mock.injector.example.ExampleExtraInterface; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import org.junit.Test; +import org.mockito.Answers; +import org.springframework.boot.test.mock.mockito.MockReset; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link DefinitionParser}. + * + * @author huzijie + * @version DefinitionParserTests.java, v 0.1 2023年08月21日 2:57 PM huzijie Exp $ + */ +public class DefinitionParserTests { + + private final DefinitionParser parser = new DefinitionParser(); + + @Test + public void parseSingleMockBeanInjector() { + this.parser.parse(SingleMockBeanInjector.class); + assertThat(getDefinitions()).hasSize(1); + assertThat(getMockDefinition(0).getMockType().resolve()).isEqualTo(ExampleService.class); + } + + @Test + public void parseMockBeanInjectorAttributes() { + this.parser.parse(MockBeanInjectorAttributes.class); + assertThat(getDefinitions()).hasSize(1); + MockDefinition definition = getMockDefinition(0); + assertThat(definition.getField()).isEqualTo("Field"); + assertThat(definition.getName()).isEqualTo("Name"); + assertThat(definition.getModule()).isEqualTo("Module"); + assertThat(definition.getMockType().resolve()).isEqualTo(ExampleService.class); + assertThat(definition.getExtraInterfaces()).containsExactly(ExampleExtraInterface.class); + assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); + assertThat(definition.isSerializable()).isTrue(); + assertThat(definition.getReset()).isEqualTo(MockReset.NONE); + assertThat(definition.getQualifier()).isNull(); + } + + @Test + public void parseDuplicateMockBeanInjector() { + assertThatIllegalStateException().isThrownBy(() -> this.parser.parse(DuplicateMockBeanInjector.class)) + .withMessageContaining("Duplicate mock definition"); + } + + @Test + public void parseSingleSpyBeanInjector() { + this.parser.parse(SingleSpyBeanInjector.class); + assertThat(getDefinitions()).hasSize(1); + assertThat(getSpyDefinition(0).getMockType().resolve()).isEqualTo(ExampleService.class); + } + + @Test + public void parseSpyBeanInjectorAttributes() { + this.parser.parse(SpyBeanInjectorAttributes.class); + assertThat(getDefinitions()).hasSize(1); + SpyDefinition definition = getSpyDefinition(0); + assertThat(definition.getField()).isEqualTo("Field"); + assertThat(definition.getName()).isEqualTo("Name"); + assertThat(definition.getModule()).isEqualTo("Module"); + assertThat(definition.getMockType().resolve()).isEqualTo(ExampleService.class); + assertThat(definition.getReset()).isEqualTo(MockReset.NONE); + assertThat(definition.getQualifier()).isNull(); + } + + private MockDefinition getMockDefinition(int index) { + return (MockDefinition) getDefinitions().get(index); + } + + private SpyDefinition getSpyDefinition(int index) { + return (SpyDefinition) getDefinitions().get(index); + } + + private List getDefinitions() { + return new ArrayList<>(this.parser.getDefinitions()); + } + + static class SingleMockBeanInjector { + + @MockBeanInjector(field = "exampleService", type = ExampleService.class) + private ExampleService exampleService; + + } + + static class MockBeanInjectorAttributes { + + @MockBeanInjector(field = "Field", module = "Module", name = "Name", type = ExampleService.class, extraInterfaces = ExampleExtraInterface.class, answer = Answers.RETURNS_SMART_NULLS, serializable = true, reset = MockReset.NONE) + private ExampleService exampleService; + } + + static class DuplicateMockBeanInjector { + + @MockBeanInjector(field = "exampleService", type = ExampleService.class) + private ExampleService exampleServiceA; + + @MockBeanInjector(field = "exampleService", type = ExampleService.class) + private ExampleService exampleServiceB; + } + + static class SingleSpyBeanInjector { + + @SpyBeanInjector(field = "exampleService", type = ExampleService.class) + private ExampleService exampleService; + } + + static class SpyBeanInjectorAttributes { + + @SpyBeanInjector(field = "Field", module = "Module", name = "Name", type = ExampleService.class, reset = MockReset.NONE, proxyTargetAware = false) + private ExampleService exampleService; + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolverTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolverTests.java new file mode 100644 index 000000000..c6c1b65a0 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolverTests.java @@ -0,0 +1,400 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.test.mock.injector.resolver; + +import com.alipay.sofa.boot.constant.SofaBootConstants; +import com.alipay.sofa.isle.IsleDeploymentModel; +import com.alipay.sofa.test.mock.injector.definition.Definition; +import com.alipay.sofa.test.mock.injector.definition.MockDefinition; +import com.alipay.sofa.test.mock.injector.definition.QualifierDefinition; +import com.alipay.sofa.test.mock.injector.definition.SpyDefinition; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.RealExampleService; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.ResolvableType; +import org.springframework.util.ReflectionUtils; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link BeanInjectorResolver}. + * + * @author huzijie + * @version BeanInjectorResolverTests.java, v 0.1 2023年08月21日 5:35 PM huzijie Exp $ + */ +public class BeanInjectorResolverTests { + + @Test + public void targetModuleExist() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + TargetClass.class); + IsleDeploymentModel isleDeploymentModel = mock(IsleDeploymentModel.class); + Map map = new HashMap(); + map.put("testModule", applicationContext); + when(isleDeploymentModel.getModuleApplicationContextMap()).thenReturn(map); + applicationContext.getBeanFactory().registerSingleton(SofaBootConstants.APPLICATION, + isleDeploymentModel); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition(ResolvableType.forClass(ExampleService.class), + "targetClass", null, "testModule", "exampleService", null, null, false, null, null); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + TargetClass targetClass = applicationContext.getBean(TargetClass.class); + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isMock()).isTrue(); + } + + @Test + public void targetModuleNotExist() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext(TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition( + ResolvableType.forClass(ExampleService.class), "targetClass", null, "testModule", "exampleService", + null, null, false, null, null); + assertThatIllegalStateException().isThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .withMessageContaining("Unable to find target module [testModule] when resolve injector"); + } + + @Test + public void findTargetBeanByName() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition(ResolvableType.forClass(ExampleService.class), + "targetClass", null, null, "exampleService", null, null, false, null, null); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + TargetClass targetClass = applicationContext.getBean(TargetClass.class); + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isMock()).isTrue(); + } + + @Test + public void findTargetBeanByNameButNoBeanExist() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext(TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition( + ResolvableType.forClass(ExampleService.class), "noExistBean", null, null, "exampleService", + null, null, false, null, null); + assertThatIllegalStateException().isThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .withMessageContaining("Unable to create bean injector to bean [noExistBean] target bean not exist"); + } + + @Test + public void findTargetBeanByClass() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition(ResolvableType.forClass(ExampleService.class), + null, ResolvableType.forClass(TargetClass.class), null, "exampleService", null, null, + false, null, null); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + TargetClass targetClass = applicationContext.getBean(TargetClass.class); + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isMock()).isTrue(); + } + + @Test + public void findTargetBeanByClassButNoBeanExist() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext(TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition( + ResolvableType.forClass(ExampleService.class), null, ResolvableType.forClass(ExampleService.class), null, "exampleService", + null, null, false, null, null); + assertThatIllegalStateException().isThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .withMessageContaining("expected a single matching bean to injector but no bean found"); + } + + @Test + public void findTargetBeanByClassButNoBeanExistCausedByScope() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScopeTargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition( + ResolvableType.forClass(ExampleService.class), null, ResolvableType.forClass(ExampleService.class), null, "exampleService", + null, null, false, null, null); + assertThatIllegalStateException().isThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .withMessageContaining("expected a single matching bean to injector but no bean found"); + } + + @Test + public void findTargetBeanByClassButMultiBeanFound() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext(MultiTargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition( + ResolvableType.forClass(ExampleService.class), null, ResolvableType.forClass(TargetClass.class), null, "exampleService", + null, null, false, null, null); + assertThatIllegalStateException().isThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .withMessageContaining("expected a single matching bean to injector but found [targetClassA, targetClassB]"); + } + + @Test + public void findTargetBeanByClassWithQualifier() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + MultiTargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition(ResolvableType.forClass(ExampleService.class), + null, ResolvableType.forClass(TargetClass.class), null, "exampleService", null, null, + false, null, QualifierDefinition.forElement(ReflectionUtils.findField( + QualifierClass.class, "targetClassField"))); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + TargetClass targetClass = applicationContext.getBean("targetClassA", TargetClass.class); + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isMock()).isTrue(); + } + + @Test + public void findTargetBeanByClassWithPrimary() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + OnePrimaryTargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition(ResolvableType.forClass(ExampleService.class), + null, ResolvableType.forClass(TargetClass.class), null, "exampleService", null, null, + false, null, null); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + TargetClass targetClass = applicationContext.getBean("targetClassA", TargetClass.class); + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isMock()).isTrue(); + } + + @Test + public void findTargetBeanByClassButMultiPrimaryBeanFound() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext(MultiPrimaryTargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition( + ResolvableType.forClass(ExampleService.class), null, ResolvableType.forClass(TargetClass.class), null, "exampleService", + null, null, false, null, null); + assertThatThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .isInstanceOf(NoUniqueBeanDefinitionException.class) + .hasMessageContaining("more than one 'primary' bean found among candidates: [[targetClassA, targetClassB]]"); + } + + @Test + public void targetFieldCannotBeFound() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext(TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition( + ResolvableType.forClass(ExampleService.class), "targetClass", null, null, "exampleServiceA", + null, null, false, null, null); + assertThatIllegalStateException().isThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .withMessageContaining("Unable to inject target field to bean targetClass, can not find field exampleServiceA in class com.alipay.sofa.test.mock.injector.resolver.BeanInjectorResolverTests$TargetClass"); + } + + @Test + public void jdkProxyBeanInject() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + JdkProxyTargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition(ResolvableType.forClass(ExampleService.class), + "targetClass", null, null, "exampleService", null, null, false, null, null); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + TargetInterface targetClass = applicationContext.getBean(TargetInterface.class); + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isMock()).isTrue(); + } + + @Test + public void cglibProxyBeanInject() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + CglibProxyTargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new MockDefinition(ResolvableType.forClass(ExampleService.class), + "targetClass", null, null, "exampleService", null, null, false, null, null); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + TargetInterface targetClass = applicationContext.getBean(TargetInterface.class); + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isMock()).isTrue(); + } + + @Test + public void spyTargetBeanWhenFieldIsNull() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + + Definition definition = new SpyDefinition(ResolvableType.forClass(ExampleService.class), + "targetClass", null, null, "exampleService", null, false, null); + assertThatIllegalStateException().isThrownBy(() -> beanInjectorResolver.resolveStub(definition)) + .withMessageContaining("Unable to create spy to inject target field private com.alipay.sofa.test.mock.injector.example.ExampleService com.alipay.sofa.test.mock.injector.resolver.BeanInjectorResolverTests$TargetClass.exampleService when origin value is null"); + } + + @Test + public void spyTargetBean() { + GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext( + TargetClass.class); + BeanInjectorResolver beanInjectorResolver = new BeanInjectorResolver(applicationContext); + TargetClass targetClass = applicationContext.getBean(TargetClass.class); + targetClass.setExampleService(new RealExampleService("test")); + + Definition definition = new SpyDefinition(ResolvableType.forClass(ExampleService.class), + "targetClass", null, null, "exampleService", null, false, null); + BeanInjectorStub stub = beanInjectorResolver.resolveStub(definition); + stub.inject(); + + assertThat(Mockito.mockingDetails(targetClass.getExampleService()).isSpy()).isTrue(); + } + + interface TargetInterface { + + ExampleService getExampleService(); + + } + + @Configuration(value = "targetClass", proxyBeanMethods = false) + static class TargetClass implements TargetInterface { + + private ExampleService exampleService; + + public ExampleService getExampleService() { + return exampleService; + } + + public void setExampleService(ExampleService exampleService) { + this.exampleService = exampleService; + } + } + + @Configuration + static class MultiTargetClass { + + @Bean + public TargetClass targetClassA() { + return new TargetClass(); + } + + @Bean + public TargetClass targetClassB() { + return new TargetClass(); + } + } + + static class QualifierClass { + + @Qualifier("targetClassA") + private TargetClass targetClassField; + } + + @Configuration + static class ScopeTargetClass { + + @Bean + @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) + public TargetClass targetClass() { + return new TargetClass(); + } + } + + @Configuration + static class OnePrimaryTargetClass { + + @Bean + @Primary + public TargetClass targetClassA() { + return new TargetClass(); + } + + @Bean + public TargetClass targetClassB() { + return new TargetClass(); + } + } + + @Configuration + static class MultiPrimaryTargetClass { + + @Bean + @Primary + public TargetClass targetClassA() { + return new TargetClass(); + } + + @Bean + @Primary + public TargetClass targetClassB() { + return new TargetClass(); + } + } + + @Configuration + static class JdkProxyTargetClass { + + @Bean + public TargetInterface targetClass() { + return new TargetClass(); + } + + @Bean + public BeanNameAutoProxyCreator beanNameAutoProxyCreator() { + BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); + beanNameAutoProxyCreator.setBeanNames("targetClass"); + beanNameAutoProxyCreator.setProxyTargetClass(false); + return beanNameAutoProxyCreator; + } + } + + @Configuration + static class CglibProxyTargetClass { + + @Bean + public TargetInterface targetClass() { + return new TargetClass(); + } + + @Bean + public BeanNameAutoProxyCreator beanNameAutoProxyCreator() { + BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); + beanNameAutoProxyCreator.setBeanNames("targetClass"); + beanNameAutoProxyCreator.setProxyTargetClass(true); + return beanNameAutoProxyCreator; + } + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStubTests.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStubTests.java new file mode 100644 index 000000000..f16381beb --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStubTests.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.test.mock.injector.resolver; + +import com.alipay.sofa.test.mock.injector.definition.MockDefinition; +import com.alipay.sofa.test.mock.injector.definition.SpyDefinition; +import com.alipay.sofa.test.mock.injector.example.ExampleService; +import com.alipay.sofa.test.mock.injector.example.RealExampleService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link BeanInjectorStub}. + * + * @author huzijie + * @version BeanInjectorStubTests.java, v 0.1 2023年08月21日 4:27 PM huzijie Exp $ + */ +@RunWith(MockitoJUnitRunner.class) +public class BeanInjectorStubTests { + + @Mock + private MockDefinition mockDefinition; + + @Mock + private SpyDefinition spyDefinition; + + private final Field field = ReflectionUtils.findField(TargetClass.class, + "exampleService"); + + private final ExampleService exampleService = new RealExampleService("real"); + + @Test + public void mockBeanInjectorStub() { + TargetClass targetClass = new TargetClass(); + BeanInjectorStub beanInjectorStub = new BeanInjectorStub(mockDefinition, field, targetClass); + assertThat(targetClass.getExampleService()).isNull(); + + when(mockDefinition.createMock()).thenReturn(exampleService); + beanInjectorStub.inject(); + + assertThat(targetClass.getExampleService()).isEqualTo(exampleService); + + beanInjectorStub.reset(); + assertThat(targetClass.getExampleService()).isNull(); + } + + @Test + public void spyBeanInjectorStub() { + TargetClass targetClass = new TargetClass(); + RealExampleService realExampleService = new RealExampleService("real"); + targetClass.setExampleService(realExampleService); + BeanInjectorStub beanInjectorStub = new BeanInjectorStub(spyDefinition, field, targetClass); + + when(spyDefinition.createSpy(any())).thenReturn(exampleService); + beanInjectorStub.inject(); + + assertThat(targetClass.getExampleService()).isEqualTo(exampleService); + + beanInjectorStub.reset(); + assertThat(targetClass.getExampleService()).isEqualTo(realExampleService); + } + + static class TargetClass { + + public void setExampleService(ExampleService exampleService) { + this.exampleService = exampleService; + } + + private ExampleService exampleService; + + public ExampleService getExampleService() { + return exampleService; + } + } +} diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/config/application.properties b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/config/application.properties new file mode 100644 index 000000000..8a067cade --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/config/application.properties @@ -0,0 +1,4 @@ +spring.application.name=smoke-tests-test +logging.path=./logs + + diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/logback.xml b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/logback.xml new file mode 100644 index 000000000..9b6f95322 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + + %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n + + + + + + + + + \ No newline at end of file From aded9510d2eb8e9eab256fd397488b4da4171f19 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Thu, 31 Aug 2023 14:06:04 +0800 Subject: [PATCH 13/47] Add mock inject error code (#1241) * add errorcode for exception when use injector mock --- .../InjectorMockTestExecutionListener.java | 4 ++-- .../injector/resolver/BeanInjectorResolver.java | 16 ++++++---------- .../mock/injector/resolver/BeanInjectorStub.java | 4 ++-- .../resources/sofa-boot/log-codes.properties | 8 ++++++++ 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.java index 98d01ab71..9dacebe9c 100644 --- a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.java +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/InjectorMockTestExecutionListener.java @@ -16,6 +16,7 @@ */ package com.alipay.sofa.test.mock.injector; +import com.alipay.sofa.boot.error.ErrorCode; import com.alipay.sofa.test.mock.injector.annotation.MockBeanInjector; import com.alipay.sofa.test.mock.injector.annotation.SpyBeanInjector; import com.alipay.sofa.test.mock.injector.definition.Definition; @@ -85,8 +86,7 @@ private void injectTestClass(DefinitionParser parser, TestContext testContext) { if (existingValue == injectValue) { return; } - Assert.state(existingValue == null, () -> "The existing value '" + existingValue + "' of field '" + field - + "' is not the same as the new value '" + injectValue + "'"); + Assert.state(existingValue == null, () -> ErrorCode.convert("01-30000", existingValue, field, injectValue)); ReflectionUtils.setField(field, target, injectValue); } }); diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java index 6418fad25..0852fd357 100644 --- a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorResolver.java @@ -17,6 +17,7 @@ package com.alipay.sofa.test.mock.injector.resolver; import com.alipay.sofa.boot.constant.SofaBootConstants; +import com.alipay.sofa.boot.error.ErrorCode; import com.alipay.sofa.isle.IsleDeploymentModel; import com.alipay.sofa.test.mock.injector.definition.Definition; import com.alipay.sofa.test.mock.injector.definition.QualifierDefinition; @@ -85,8 +86,7 @@ public BeanInjectorStub resolveStub(Definition definition) { // find target bean instance if (!beanFactory.containsBean(beanName)) { - throw new IllegalStateException("Unable to create bean injector to bean [" + beanName - + "] target bean not exist"); + throw new IllegalStateException(ErrorCode.convert("01-30005", beanName)); } Object bean = resolveTargetObject(beanFactory.getBean(beanName)); @@ -99,8 +99,7 @@ private ApplicationContext getApplicationContext(Definition definition) { if (StringUtils.hasText(module)) { ApplicationContext applicationContext = isleApplicationContexts.get(module); if (applicationContext == null) { - throw new IllegalStateException("Unable to find target module [" + module - + "] when resolve injector: " + definition); + throw new IllegalStateException(ErrorCode.convert("01-30002", module, definition)); } return applicationContext; } else { @@ -140,9 +139,7 @@ private String getBeanName(ConfigurableListableBeanFactory beanFactory, Definiti Set existingBeans = getExistingBeans(beanFactory, definition.getType(), definition.getQualifier()); if (existingBeans.isEmpty()) { - throw new IllegalStateException( - "Unable to create bean injector to bean by type [" + definition.getType() - + "] expected a single matching bean to injector but no bean found"); + throw new IllegalStateException(ErrorCode.convert("01-30003", definition.getType())); } if (existingBeans.size() == 1) { return existingBeans.iterator().next(); @@ -152,9 +149,8 @@ private String getBeanName(ConfigurableListableBeanFactory beanFactory, Definiti if (primaryCandidate != null) { return primaryCandidate; } - throw new IllegalStateException( - "Unable to create bean injector to bean by type [" + definition.getType() - + "] expected a single matching bean to injector but found " + existingBeans); + throw new IllegalStateException(ErrorCode.convert("01-30004", definition.getType(), + existingBeans)); } private Set getExistingBeans(ConfigurableListableBeanFactory beanFactory, diff --git a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.java b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.java index 8b2bcb6de..72f63bfd2 100644 --- a/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.java +++ b/sofa-boot-project/sofa-boot-core/test-sofa-boot/src/main/java/com/alipay/sofa/test/mock/injector/resolver/BeanInjectorStub.java @@ -16,6 +16,7 @@ */ package com.alipay.sofa.test.mock.injector.resolver; +import com.alipay.sofa.boot.error.ErrorCode; import com.alipay.sofa.test.mock.injector.definition.Definition; import com.alipay.sofa.test.mock.injector.definition.MockDefinition; import com.alipay.sofa.test.mock.injector.definition.SpyDefinition; @@ -58,8 +59,7 @@ public BeanInjectorStub(Definition definition, Field field, Object bean) { ReflectionUtils.makeAccessible(field); this.originalValue = ReflectionUtils.getField(field, bean); if (definition instanceof SpyDefinition && this.originalValue == null) { - throw new IllegalStateException("Unable to create spy to inject target field " + field - + " when origin value is null"); + throw new IllegalStateException(ErrorCode.convert("01-30001", field)); } } diff --git a/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties b/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties index 29bd089a8..5920a0a72 100644 --- a/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties +++ b/sofa-boot-project/sofa-boot/src/main/resources/sofa-boot/log-codes.properties @@ -100,6 +100,14 @@ 01-24001=SOFABoot ReadinessCheckCallback[%s] check failed, the details is: %s 01-24002=Error occurred while doing ReadinessCheckCallback[%s] check +#Test component error +01-30000=The existing value '%s' of field '%s' is not the same as the new value '%s' +01-30001=Unable to create spy to inject target field %s when origin value is null +01-30002=Unable to find target module [%s] when resolve injector: %s +01-30003=Unable to create bean injector to bean by type [%s] expected a single matching bean to injector but no bean found +01-30004=Unable to create bean injector to bean by type [%s] expected a single matching bean to injector but found %s +01-30005=Unable to create bean injector to bean [%s] target bean not exist + From 3aa6a45288debcb3a8b602c07ebe32406529ba9e Mon Sep 17 00:00:00 2001 From: Dando Date: Fri, 1 Sep 2023 11:06:58 +0800 Subject: [PATCH 14/47] release_3.20.0 (#1243) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 寻芳 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 16713a885..c76eab7b2 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.20.0-SNAPSHOT + 3.20.0 ${revision} 1.6.7 From efbf96cc41f3a51c5743c10594261c6ed4c0f905 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Thu, 28 Sep 2023 10:25:18 +0800 Subject: [PATCH 15/47] Update 3.21.0-SNAPSHOT (#1250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * init interface class canonical name to avoid multi reflection * update 3.21.0-SNAPSHOT --------- Co-authored-by: 致节 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c76eab7b2..b4750d3fc 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.20.0 + 3.21.0-SNAPSHOT ${revision} 1.6.7 From d03eeea46f28c0e09f69be0b2937c6b1ba01610d Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Thu, 28 Sep 2023 10:25:28 +0800 Subject: [PATCH 16/47] Merge #1246 (#1251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * init interface class canonical name to avoid multi reflection * update 3.21.0-SNAPSHOT * avoid SofaConfigSourceSupportListener duplicate register configs (#1246) * format --------- Co-authored-by: 致节 --- .../SofaConfigSourceSupportListener.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/listener/SofaConfigSourceSupportListener.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/listener/SofaConfigSourceSupportListener.java index f730cf561..356f5db7c 100644 --- a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/listener/SofaConfigSourceSupportListener.java +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/listener/SofaConfigSourceSupportListener.java @@ -25,6 +25,8 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.util.StringUtils; +import java.util.concurrent.atomic.AtomicBoolean; + /** * add a config source based on {@link ConfigurableEnvironment} * @author huzijie @@ -34,32 +36,40 @@ public class SofaConfigSourceSupportListener implements ApplicationListener, Ordered { - private static final int SOFA_BOOT_CONFIG_SOURCE_ORDER = ApplicationListenerOrderConstants.SOFA_CONFIG_SOURCE_SUPPORT_LISTENER_ORDER; + private static final int SOFA_BOOT_CONFIG_SOURCE_ORDER = ApplicationListenerOrderConstants.SOFA_CONFIG_SOURCE_SUPPORT_LISTENER_ORDER; + + private final AtomicBoolean registered = new AtomicBoolean(); @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { - ConfigurableEnvironment environment = event.getEnvironment(); - SofaConfigs.addConfigSource(new AbstractConfigSource() { - @Override - public int getOrder() { - return SOFA_BOOT_CONFIG_SOURCE_ORDER; - } + registerSofaConfigs(event.getEnvironment()); + } + + private void registerSofaConfigs(ConfigurableEnvironment environment) { + if (registered.compareAndSet(false, true)) { + SofaConfigs.addConfigSource(new AbstractConfigSource() { + + @Override + public int getOrder() { + return SOFA_BOOT_CONFIG_SOURCE_ORDER; + } - @Override - public String getName() { - return "SOFABootEnv"; - } + @Override + public String getName() { + return "SOFABootEnv"; + } - @Override - public String doGetConfig(String key) { - return environment.getProperty(key); - } + @Override + public String doGetConfig(String key) { + return environment.getProperty(key); + } - @Override - public boolean hasKey(String key) { - return !StringUtils.isEmpty(environment.getProperty(key)); - } - }); + @Override + public boolean hasKey(String key) { + return !StringUtils.isEmpty(environment.getProperty(key)); + } + }); + } } @Override From ced162297c8c18c82b95a32bdd5bd3189fc73f8f Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Thu, 28 Sep 2023 10:25:38 +0800 Subject: [PATCH 17/47] Merge 1247 (#1252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * init interface class canonical name to avoid multi reflection * Fix the issue where @SofaService annotation cannot set a custom thread pool when the interfaceType is empty (#1247) --------- Co-authored-by: 致节 --- .../converter/RpcBindingConverter.java | 2 +- .../rpc/boot/test/SofaBootRpcAllTest.java | 8 ++++- .../ThreadPoolServiceAnnotationImpl.java | 33 +++++++++++++++++++ .../src/test/resources/spring/test_all.xml | 4 +++ .../spi/service/BindingConverterContext.java | 10 ++++++ .../ServiceBeanFactoryPostProcessor.java | 10 ++++-- 6 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/bean/threadpool/ThreadPoolServiceAnnotationImpl.java diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/runtime/converter/RpcBindingConverter.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/runtime/converter/RpcBindingConverter.java index 05dbbaa1f..08ef1c5fb 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/runtime/converter/RpcBindingConverter.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/runtime/converter/RpcBindingConverter.java @@ -455,7 +455,7 @@ protected void convertServiceAnnotation(RpcBindingParam bindingParam, UserThreadPool threadPoolObj = (UserThreadPool) applicationContext.getBean(threadPool); - String interfaceName = sofaServiceAnnotation.interfaceType().getCanonicalName(); + String interfaceName = bindingConverterContext.getInterfaceType().getCanonicalName(); String uniqId = sofaServiceAnnotation.uniqueId(); String uniqueName = interfaceName + ":1.0" + (StringUtils.isEmpty(uniqId) ? "" : ":" + uniqId); diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaBootRpcAllTest.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaBootRpcAllTest.java index 6a1920220..dee71cf06 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaBootRpcAllTest.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaBootRpcAllTest.java @@ -105,8 +105,13 @@ public class SofaBootRpcAllTest { private GenericService genericService; @Autowired + @Qualifier("threadPoolService") private ThreadPoolService threadPoolService; + @Autowired + @Qualifier("threadPoolAnnotationService") + private ThreadPoolService threadPoolAnnotationService; + @Autowired private RestService restService; @@ -233,7 +238,8 @@ public void testGeneric() { public void testThreadPool() { Assert.assertTrue(threadPoolService.sayThreadPool("threadPool").startsWith( "threadPool[SOFA-customerThreadPool_name")); - + Assert.assertTrue(threadPoolAnnotationService.sayThreadPool("threadPool").startsWith( + "threadPool[SOFA-customerThreadPool_name")); } @Test diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/bean/threadpool/ThreadPoolServiceAnnotationImpl.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/bean/threadpool/ThreadPoolServiceAnnotationImpl.java new file mode 100644 index 000000000..c4df7a09b --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/bean/threadpool/ThreadPoolServiceAnnotationImpl.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.rpc.boot.test.bean.threadpool; + +import com.alipay.sofa.runtime.api.annotation.SofaServiceBean; +import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; + +/** + * @author huzijie + * @version ThreadPoolServiceAnnotationImpl.java, v 0.1 2023年09月19日 6:05 PM huzijie Exp $ + */ +@SofaServiceBean(uniqueId = "annotation", bindings = { @SofaServiceBinding(bindingType = "bolt", userThreadPool = "customerThreadPool") }) +public class ThreadPoolServiceAnnotationImpl implements ThreadPoolService { + + @Override + public String sayThreadPool(String string) { + return string + "[" + Thread.currentThread().getName() + "]"; + } +} diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/resources/spring/test_all.xml b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/resources/spring/test_all.xml index f8914724d..6be043ae2 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/resources/spring/test_all.xml +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/resources/spring/test_all.xml @@ -106,6 +106,10 @@ interface="com.alipay.sofa.rpc.boot.test.bean.threadpool.ThreadPoolService"> + + + diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/service/BindingConverterContext.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/service/BindingConverterContext.java index a978f4b62..d502cfd75 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/service/BindingConverterContext.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/spi/service/BindingConverterContext.java @@ -39,6 +39,8 @@ public class BindingConverterContext { private String repeatReferLimit; + private Class interfaceType; + public ClassLoader getAppClassLoader() { return appClassLoader; } @@ -94,4 +96,12 @@ public String getRepeatReferLimit() { public void setRepeatReferLimit(String repeatReferLimit) { this.repeatReferLimit = repeatReferLimit; } + + public Class getInterfaceType() { + return interfaceType; + } + + public void setInterfaceType(Class interfaceType) { + this.interfaceType = interfaceType; + } } 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 8b2eb940d..8522e39bc 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 @@ -324,8 +324,10 @@ private void generateSofaServiceDefinition(String beanId, SofaService sofaServic interfaceType); builder.addPropertyValue(AbstractContractDefinitionParser.UNIQUE_ID_PROPERTY, sofaServiceAnnotation.uniqueId()); - builder.addPropertyValue(AbstractContractDefinitionParser.BINDINGS, - getSofaServiceBinding(sofaServiceAnnotation, sofaServiceAnnotation.bindings())); + builder.addPropertyValue( + AbstractContractDefinitionParser.BINDINGS, + getSofaServiceBinding(sofaServiceAnnotation, sofaServiceAnnotation.bindings(), + interfaceType)); builder.addPropertyReference(ServiceDefinitionParser.REF, beanId); builder.addPropertyValue(ServiceDefinitionParser.BEAN_ID, beanId); builder.addPropertyValue(AbstractContractDefinitionParser.DEFINITION_BUILDING_API_TYPE, @@ -342,7 +344,8 @@ private void generateSofaServiceDefinition(String beanId, SofaService sofaServic } private List getSofaServiceBinding(SofaService sofaServiceAnnotation, - SofaServiceBinding[] sofaServiceBindings) { + SofaServiceBinding[] sofaServiceBindings, + Class interfaceType) { List bindings = new ArrayList<>(); for (SofaServiceBinding sofaServiceBinding : sofaServiceBindings) { BindingConverter bindingConverter = bindingConverterFactory @@ -356,6 +359,7 @@ private List getSofaServiceBinding(SofaService sofaServiceAnnotation, bindingConverterContext.setApplicationContext(applicationContext); bindingConverterContext.setAppName(sofaRuntimeContext.getAppName()); bindingConverterContext.setAppClassLoader(sofaRuntimeContext.getAppClassLoader()); + bindingConverterContext.setInterfaceType(interfaceType); Binding binding = bindingConverter.convert(sofaServiceAnnotation, sofaServiceBinding, bindingConverterContext); bindings.add(binding); From 3b803af1af04885655828b83d70b0479c0327295 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Mon, 23 Oct 2023 17:06:09 +0800 Subject: [PATCH 18/47] Cherrypick 1254 (#1260) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * init interface class canonical name to avoid multi reflection * Support sofa-rpc provider register blacklist/whitelist (#1254) --------- Co-authored-by: 致节 --- .../rpc/SofaRpcAutoConfiguration.java | 9 ++- .../boot/config/SofaBootRpcProperties.java | 21 +++++ .../container/ProviderConfigContainer.java | 69 ++++++++++++++--- .../test/SofaRpcTestAutoConfiguration.java | 10 ++- ...BlackListProviderConfigContainerTests.java | 37 +++++++++ .../ProviderConfigContainerTestBase.java | 76 +++++++++++++++++++ ...WhiteListProviderConfigContainerTests.java | 37 +++++++++ 7 files changed, 246 insertions(+), 13 deletions(-) create mode 100644 sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/BlackListProviderConfigContainerTests.java create mode 100644 sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/ProviderConfigContainerTestBase.java create mode 100644 sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/WhiteListProviderConfigContainerTests.java 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 b32226369..a60ee4b1d 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 @@ -77,8 +77,13 @@ public class SofaRpcAutoConfiguration { @Bean @ConditionalOnMissingBean - public ProviderConfigContainer providerConfigContainer() { - return new ProviderConfigContainer(); + public ProviderConfigContainer providerConfigContainer(SofaBootRpcProperties sofaBootRpcProperties) { + ProviderConfigContainer providerConfigContainer = new ProviderConfigContainer(); + providerConfigContainer.setProviderRegisterWhiteList(sofaBootRpcProperties + .getProviderRegisterWhiteList()); + providerConfigContainer.setProviderRegisterBlackList(sofaBootRpcProperties + .getProviderRegisterBlackList()); + return providerConfigContainer; } @Bean diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcProperties.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcProperties.java index f0d56f4c7..f640cb74b 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcProperties.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/main/java/com/alipay/sofa/rpc/boot/config/SofaBootRpcProperties.java @@ -24,6 +24,7 @@ import org.springframework.util.StringUtils; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -341,6 +342,10 @@ public class SofaBootRpcProperties implements EnvironmentAware { */ private String dynamicConfig; + private List providerRegisterWhiteList; + + private List providerRegisterBlackList; + public String getAftRegulationEffective() { return StringUtils.isEmpty(aftRegulationEffective) ? getDotString(new Object() { }.getClass().getEnclosingMethod().getName()) : aftRegulationEffective; @@ -938,6 +943,22 @@ public void setBoltProcessInIoThread(Boolean boltProcessInIoThread) { this.boltProcessInIoThread = boltProcessInIoThread; } + public List getProviderRegisterWhiteList() { + return providerRegisterWhiteList; + } + + public void setProviderRegisterWhiteList(List providerRegisterWhiteList) { + this.providerRegisterWhiteList = providerRegisterWhiteList; + } + + public List getProviderRegisterBlackList() { + return providerRegisterBlackList; + } + + public void setProviderRegisterBlackList(List providerRegisterBlackList) { + this.providerRegisterBlackList = providerRegisterBlackList; + } + @Override public void setEnvironment(Environment environment) { this.environment = environment; 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 aa47214f5..582ba3fcf 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,14 +16,6 @@ */ package com.alipay.sofa.rpc.boot.container; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.slf4j.Logger; -import org.springframework.util.StringUtils; - 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 +25,14 @@ import com.alipay.sofa.rpc.registry.Registry; import com.alipay.sofa.rpc.registry.RegistryFactory; import com.alipay.sofa.runtime.spi.binding.Contract; +import org.slf4j.Logger; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * ProviderConfig持有者.维护编程界面级别的RPC组件。 @@ -48,6 +48,10 @@ public class ProviderConfigContainer { */ private boolean allowPublish = false; + private List providerRegisterWhiteList; + + private List providerRegisterBlackList; + /** * ProviderConfig 缓存 */ @@ -73,6 +77,23 @@ public void addProviderConfig(String key, ProviderConfig providerConfig) { } } + private boolean allowProviderRegister(ProviderConfig providerConfig) { + if (CollectionUtils.isEmpty(providerRegisterWhiteList) + && CollectionUtils.isEmpty(providerRegisterBlackList)) { + return true; + } + String uniqueName = createUniqueNameByProvider(providerConfig); + if (!CollectionUtils.isEmpty(providerRegisterBlackList) + && providerRegisterBlackList.contains(uniqueName)) { + return false; + } + if (!CollectionUtils.isEmpty(providerRegisterWhiteList) + && !providerRegisterWhiteList.contains(uniqueName)) { + return false; + } + return true; + } + /** * 获取 ProviderConfig * @@ -110,7 +131,11 @@ public void publishAllProviderConfig() { ServerConfig serverConfig = (ServerConfig) providerConfig.getServer().get(0); if (!serverConfig.getProtocol().equalsIgnoreCase( SofaBootRpcConfigConstants.RPC_PROTOCOL_DUBBO)) { - providerConfig.setRegister(true); + if (allowProviderRegister(providerConfig)) { + providerConfig.setRegister(true); + } else { + LOGGER.info("Provider will not register: [{}]", providerConfig.buildKey()); + } List registrys = providerConfig.getRegistry(); for (RegistryConfig registryConfig : registrys) { @@ -206,4 +231,30 @@ public String createUniqueName(Contract contract, RpcBinding binding) { .append(uniqueId).append(protocol).toString(); } + /** + * Create UniqueName by interfaceId and uniqueId + */ + private String createUniqueNameByProvider(ProviderConfig providerConfig) { + String uniqueId = ""; + if (StringUtils.hasText(providerConfig.getUniqueId())) { + uniqueId = ":" + providerConfig.getUniqueId(); + } + return providerConfig.getInterfaceId() + uniqueId; + } + + public void setProviderRegisterWhiteList(List providerRegisterWhiteList) { + this.providerRegisterWhiteList = providerRegisterWhiteList; + } + + public void setProviderRegisterBlackList(List providerRegisterBlackList) { + this.providerRegisterBlackList = providerRegisterBlackList; + } + + public List getProviderRegisterWhiteList() { + return providerRegisterWhiteList; + } + + public List getProviderRegisterBlackList() { + return providerRegisterBlackList; + } } diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaRpcTestAutoConfiguration.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaRpcTestAutoConfiguration.java index ab3ef6601..d7ebe8a31 100644 --- a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaRpcTestAutoConfiguration.java +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/SofaRpcTestAutoConfiguration.java @@ -72,8 +72,14 @@ @EnableConfigurationProperties(SofaBootRpcProperties.class) public class SofaRpcTestAutoConfiguration { @Bean - public ProviderConfigContainer providerConfigContainer() { - return new ProviderConfigContainer(); + @ConditionalOnMissingBean + public ProviderConfigContainer providerConfigContainer(SofaBootRpcProperties sofaBootRpcProperties) { + ProviderConfigContainer providerConfigContainer = new ProviderConfigContainer(); + providerConfigContainer.setProviderRegisterWhiteList(sofaBootRpcProperties + .getProviderRegisterWhiteList()); + providerConfigContainer.setProviderRegisterBlackList(sofaBootRpcProperties + .getProviderRegisterBlackList()); + return providerConfigContainer; } @Bean diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/BlackListProviderConfigContainerTests.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/BlackListProviderConfigContainerTests.java new file mode 100644 index 000000000..fec3542dd --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/BlackListProviderConfigContainerTests.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.rpc.boot.test.provider; + +import com.alipay.sofa.rpc.core.exception.SofaRouteException; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.springframework.test.context.TestPropertySource; + +/** + * @author huzijie + * @version BlackListProviderConfigContainerTests.java, v 0.1 2023年09月27日 11:26 AM huzijie Exp $ + */ +@TestPropertySource(properties = "com.alipay.sofa.rpc.providerRegisterBlackList=com.alipay.sofa.rpc.boot.test.bean.SampleFacade:uniqueId") +public class BlackListProviderConfigContainerTests extends ProviderConfigContainerTestBase { + + @Test + public void checkProviderExported() { + Assertions.assertThat(sampleFacadeA.sayHi("Sofa")).isEqualTo("hi Sofa!"); + Assertions.assertThatThrownBy(() -> sampleFacadeB.sayHi("Sofa")).isInstanceOf(SofaRouteException.class). + hasMessageContaining("RPC-020060001"); + } +} diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/ProviderConfigContainerTestBase.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/ProviderConfigContainerTestBase.java new file mode 100644 index 000000000..d22a3c1ba --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/ProviderConfigContainerTestBase.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.rpc.boot.test.provider; + +import com.alipay.sofa.rpc.boot.container.ProviderConfigContainer; +import com.alipay.sofa.rpc.boot.test.bean.SampleFacade; +import com.alipay.sofa.rpc.boot.test.bean.SampleFacadeImpl; +import com.alipay.sofa.runtime.api.annotation.SofaReference; +import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding; +import com.alipay.sofa.runtime.api.annotation.SofaService; +import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding; +import org.junit.After; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author huzijie + * @version ProviderConfigContainerTests.java, v 0.1 2023年09月27日 11:14 AM huzijie Exp $ + */ +@SpringBootApplication +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@Import(ProviderConfigContainerTestBase.RpcPublishConfiguration.class) +public class ProviderConfigContainerTestBase { + + @SofaReference(jvmFirst = false, binding = @SofaReferenceBinding(bindingType = "bolt")) + protected SampleFacade sampleFacadeA; + + @SofaReference(jvmFirst = false, binding = @SofaReferenceBinding(bindingType = "bolt"), uniqueId = "uniqueId") + protected SampleFacade sampleFacadeB; + + @Autowired + private ProviderConfigContainer providerConfigContainer; + + @After + public void clearExported() { + providerConfigContainer.unExportAllProviderConfig(); + } + + @Configuration + static class RpcPublishConfiguration { + + @SofaService(bindings = { @SofaServiceBinding(bindingType = "bolt") }) + @Bean + public SampleFacade providerA() { + return new SampleFacadeImpl(); + } + + @SofaService(bindings = { @SofaServiceBinding(bindingType = "bolt") }, uniqueId = "uniqueId") + @Bean + public SampleFacade providerB() { + return new SampleFacadeImpl(); + } + } + +} diff --git a/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/WhiteListProviderConfigContainerTests.java b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/WhiteListProviderConfigContainerTests.java new file mode 100644 index 000000000..dc2e83191 --- /dev/null +++ b/sofa-boot-project/sofa-boot-core/rpc-sofa-boot/src/test/java/com/alipay/sofa/rpc/boot/test/provider/WhiteListProviderConfigContainerTests.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alipay.sofa.rpc.boot.test.provider; + +import com.alipay.sofa.rpc.core.exception.SofaRouteException; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.springframework.test.context.TestPropertySource; + +/** + * @author huzijie + * @version WhiteListProviderConfigContainerTests.java, v 0.1 2023年09月27日 11:27 AM huzijie Exp $ + */ +@TestPropertySource(properties = "com.alipay.sofa.rpc.providerRegisterWhiteList=com.alipay.sofa.rpc.boot.test.bean.SampleFacade:uniqueId") +public class WhiteListProviderConfigContainerTests extends ProviderConfigContainerTestBase { + + @Test + public void checkProviderExported() { + Assertions.assertThatThrownBy(() -> sampleFacadeA.sayHi("Sofa")).isInstanceOf(SofaRouteException.class). + hasMessageContaining("RPC-020060001"); + Assertions.assertThat(sampleFacadeB.sayHi("Sofa")).isEqualTo("hi Sofa!"); + } +} From 9505851e9154aca2dd54fddad5174a7ebe272e28 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Tue, 31 Oct 2023 14:49:15 +0800 Subject: [PATCH 19/47] Release 3.21.0 (#1261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * init interface class canonical name to avoid multi reflection * release 3.21.0 --------- Co-authored-by: 致节 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b4750d3fc..86d4c1dd8 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.21.0-SNAPSHOT + 3.21.0 ${revision} 1.6.7 From fcd365c416dd572187dc12598a65b3ea207c83ea Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Thu, 25 Jan 2024 15:19:26 +0800 Subject: [PATCH 20/47] Update to 3.22.0 (#1282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * support ExcludeBeanNameAutoProxyCreator (#1277) Co-authored-by: 致节 (cherry picked from commit f708c0cf757c769c932982f22c17b43f37155aeb) * 1. skip low cost spring.context.config-classes.enhance and bean-factory #1270 2. Support ExcludeBeanNameAutoProxyCreator #1277 3. avoid ReadinessCheckListener cuncrrency * relase 3.22.0 --------- Co-authored-by: 致节 --- pom.xml | 2 +- .../healthcheck/ReadinessCheckListener.java | 5 +- .../alipay/sofa/startup/StartupReporter.java | 12 ++- .../ExcludeBeanNameAutoProxyCreator.java | 74 +++++++++++++ .../ExcludeBeanNameAutoProxyCreatorTest.java | 102 ++++++++++++++++++ 5 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreator.java create mode 100644 sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreatorTest.java diff --git a/pom.xml b/pom.xml index 86d4c1dd8..1eae329ec 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.21.0 + 3.22.0 ${revision} 1.6.7 diff --git a/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/ReadinessCheckListener.java b/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/ReadinessCheckListener.java index 8b5c86a14..8efd79618 100644 --- a/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/ReadinessCheckListener.java +++ b/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/ReadinessCheckListener.java @@ -40,6 +40,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -71,11 +72,11 @@ public class ReadinessCheckListener implements ApplicationContextAware, Ordered, private boolean healthCheckerStatus = true; - private Map healthCheckerDetails = new HashMap<>(); + private Map healthCheckerDetails = new ConcurrentHashMap<>(); private boolean healthIndicatorStatus = true; - private Map healthIndicatorDetails = new HashMap<>(); + private Map healthIndicatorDetails = new ConcurrentHashMap<>(); private boolean healthCallbackStatus = true; private boolean readinessCheckFinish = false; diff --git a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/StartupReporter.java b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/StartupReporter.java index 6b8fcee80..357115845 100644 --- a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/StartupReporter.java +++ b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/StartupReporter.java @@ -52,15 +52,23 @@ 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 = new HashSet<>(); public static final Collection SPRING_CONTEXT_POST_PROCESSOR_TYPES = new HashSet<>(); + public static final Collection SPRING_CONFIG_CLASSES_ENHANCE_TYPES = new HashSet<>(); + static { SPRING_BEAN_INSTANTIATE_TYPES.add(SPRING_BEANS_INSTANTIATE); SPRING_BEAN_INSTANTIATE_TYPES.add(SPRING_BEANS_SMART_INSTANTIATE); SPRING_CONTEXT_POST_PROCESSOR_TYPES.add(SPRING_CONTEXT_BEANDEF_REGISTRY_POST_PROCESSOR); SPRING_CONTEXT_POST_PROCESSOR_TYPES.add(SPRING_CONTEXT_BEAN_FACTORY_POST_PROCESSOR); + SPRING_CONFIG_CLASSES_ENHANCE_TYPES.add(SPRING_CONFIG_CLASSES_ENHANCE); + SPRING_CONFIG_CLASSES_ENHANCE_TYPES.add(SPRING_BEAN_POST_PROCESSOR); } private final StartupStaticsModel startupStaticsModel = new StartupStaticsModel(); @@ -175,7 +183,9 @@ public List generateBeanStats(ConfigurableApplicationContext context) private boolean filterBeanInitializeByCost(BeanStat beanStat) { String name = beanStat.getBeanType(); - 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() >= beanInitCostThreshold; } else { return true; 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..7c2ee76a2 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreator.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package 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 org.springframework.util.StringUtils; + +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(StringUtils.trimWhitespace(mappedName)); + } + } + + @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/test/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreatorTest.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreatorTest.java new file mode 100644 index 000000000..083e0aa20 --- /dev/null +++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/aop/framework/autoproxy/ExcludeBeanNameAutoProxyCreatorTest.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.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 ExcludeBeanNameAutoProxyCreatorTest { + + @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"; + } + } +} From 6716b70641b44da36243d85aed1f028d5708c931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=B4=E8=8A=82?= Date: Tue, 2 Apr 2024 15:58:31 +0800 Subject: [PATCH 21/47] update 3.23.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1eae329ec..b30d18b6f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.22.0 + 3.23.0-SNAPSHOT ${revision} 1.6.7 From 9aaeaf574cc625a304c70af1dd82b325740b5664 Mon Sep 17 00:00:00 2001 From: evenliu Date: Tue, 2 Apr 2024 15:59:17 +0800 Subject: [PATCH 22/47] add queueRemainingSize log at the beginning monitor (#1292) * add queueRemainingSize at the beginning --------- Co-authored-by: liujianjun.ljj --- .../com/alipay/sofa/rpc/boot/common/RpcThreadPoolMonitor.java | 2 ++ 1 file changed, 2 insertions(+) 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"); From cc9e6cf8ff416380549568a9a1f5e7dee1124bff Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Tue, 2 Apr 2024 16:39:18 +0800 Subject: [PATCH 23/47] update springboot 2.7.18 (#1303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 致节 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b30d18b6f..0b130abe1 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.14 + 2.7.18 From 08463d387c5267b64dece1171c73c8f3c3194b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=B4=E8=8A=82?= Date: Tue, 16 Apr 2024 11:31:52 +0800 Subject: [PATCH 24/47] avoid concurrent problem in JvmFilterHolder --- .../java/com/alipay/sofa/runtime/filter/JvmFilterHolder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 e8aacb454..cfa173e0e 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,9 @@ import org.springframework.core.Ordered; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -28,7 +28,7 @@ * Created on 2020/8/18 */ public class JvmFilterHolder { - private static final List JVM_FILTERS = new ArrayList<>(); + private static final List JVM_FILTERS = new CopyOnWriteArrayList<>(); private static final AtomicBoolean filtersSorted = new AtomicBoolean(false); private static final Comparator comparator = (f1, f2) -> { From 80e8e1bd34fe3aae46591827dda3c62366e17526 Mon Sep 17 00:00:00 2001 From: huazhongming Date: Thu, 18 Apr 2024 11:17:39 +0800 Subject: [PATCH 25/47] Upgrade github action version to v4 (#1307) Signed-off-by: crazyhzm --- .github/workflows/maven.yml | 7 ++++--- .github/workflows/release.yml | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 224c63d43..5657c7b94 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -18,10 +18,11 @@ jobs: matrix: jdk: [8, 11] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: 'temurin' java-version: ${{ matrix.jdk }} - name: Install Zookeeper run: echo "Install Zookeeper 3.5.6" @@ -34,7 +35,7 @@ jobs: && sh ./tools/check_format.sh && mvn clean test -Pdefault - 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 a4fc69e1f..b59968913 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 8 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '8' distribution: 'temurin' From 2c4ea1843af68dcbcbde5edd5fb0d559c390f7f8 Mon Sep 17 00:00:00 2001 From: huazhongming Date: Mon, 22 Apr 2024 10:51:05 +0800 Subject: [PATCH 26/47] Support mac M1(osx-aarch_64) compile and test (#1306) Signed-off-by: crazyhzm --- sofa-boot-project/sofa-boot-core/rpc-sofa-boot/pom.xml | 3 ++- sofa-boot-project/sofaboot-dependencies/pom.xml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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 a2bfc2c88..bb491a791 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 @@ -16,6 +16,7 @@ ${basedir}/../../.. 0.0.3 3.7.0 + 3.17.3 @@ -193,7 +194,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/sofaboot-dependencies/pom.xml b/sofa-boot-project/sofaboot-dependencies/pom.xml index 610f72f89..e5b5e4904 100644 --- a/sofa-boot-project/sofaboot-dependencies/pom.xml +++ b/sofa-boot-project/sofaboot-dependencies/pom.xml @@ -53,7 +53,7 @@ 1.0.0 2.0.0 3.11.0 - 1.28.0 + 1.53.0 1.17.0 From e36ebc59696a6524f326e4de89d77767f8de8056 Mon Sep 17 00:00:00 2001 From: huazhongming Date: Mon, 22 Apr 2024 10:55:47 +0800 Subject: [PATCH 27/47] Fix readiness health check list mistake in parallel checking scenario (#1309) Signed-off-by: crazyhzm --- .../com/alipay/sofa/healthcheck/HealthCheckerProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/HealthCheckerProcessor.java b/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/HealthCheckerProcessor.java index 2b4412904..aed67769f 100644 --- a/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/HealthCheckerProcessor.java +++ b/sofa-boot-project/sofa-boot-core/healthcheck-sofa-boot/src/main/java/com/alipay/sofa/healthcheck/HealthCheckerProcessor.java @@ -158,12 +158,12 @@ 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 (healthCheckProperties.isHealthCheckParallelEnable()) { - CountDownLatch countDownLatch = new CountDownLatch(healthCheckers.size()); + CountDownLatch countDownLatch = new CountDownLatch(readinessHealthCheckers.size()); AtomicBoolean parallelResult = new AtomicBoolean(true); - healthCheckers.forEach((String key, HealthChecker value) -> healthCheckExecutor.executeTask(() -> { + readinessHealthCheckers.forEach((String key, HealthChecker value) -> healthCheckExecutor.executeTask(() -> { try { if (!doHealthCheck(key, value, false, healthMap, true, false)) { parallelResult.set(false); From 69769430f2ced3363006c14e80eaced918727910 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Mon, 22 Apr 2024 11:26:43 +0800 Subject: [PATCH 28/47] avoid concurrent problem in JvmFilterHolder (#1304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 致节 --- .../java/com/alipay/sofa/runtime/filter/JvmFilterHolder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 e8aacb454..cfa173e0e 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,9 @@ import org.springframework.core.Ordered; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -28,7 +28,7 @@ * Created on 2020/8/18 */ public class JvmFilterHolder { - private static final List JVM_FILTERS = new ArrayList<>(); + private static final List JVM_FILTERS = new CopyOnWriteArrayList<>(); private static final AtomicBoolean filtersSorted = new AtomicBoolean(false); private static final Comparator comparator = (f1, f2) -> { From 1089e7bdeec02f5437fe8887ebe79d7f359b9949 Mon Sep 17 00:00:00 2001 From: wutao Date: Thu, 25 Apr 2024 10:58:49 +0800 Subject: [PATCH 29/47] increase StartupSpringApplicationRunListener order (#1311) * increase StartupSpringApplicationRunListener order,retain more space --- .../sofa/startup/stage/StartupSpringApplicationRunListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/stage/StartupSpringApplicationRunListener.java b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/stage/StartupSpringApplicationRunListener.java index 628db78df..d91f45e22 100644 --- a/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/stage/StartupSpringApplicationRunListener.java +++ b/sofa-boot-project/sofa-boot-core/startup-sofa-boot/src/main/java/com/alipay/sofa/startup/stage/StartupSpringApplicationRunListener.java @@ -157,7 +157,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) { From d65c12beec443af5d9e6ef78578d5461b29a09ee Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Tue, 14 May 2024 10:32:51 +0800 Subject: [PATCH 30/47] Fix lazyinit value transfer problem (#1317) * Fix lazyinit value transfer problem Signed-off-by: Jermaine Hua * Add integration tests for lazyinit Signed-off-by: Jermaine Hua --------- Signed-off-by: Jermaine Hua --- .../ServiceBeanFactoryPostProcessor.java | 9 ++- .../test/SofaServiceAndReferenceTest.java | 60 ++++++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) 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 8522e39bc..8774eb6f6 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 @@ -49,6 +49,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +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; @@ -247,7 +248,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 @@ -314,7 +317,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/test/java/com/alipay/sofa/runtime/test/SofaServiceAndReferenceTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/SofaServiceAndReferenceTest.java index 043fa98b7..f2d05323c 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/SofaServiceAndReferenceTest.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/SofaServiceAndReferenceTest.java @@ -29,6 +29,7 @@ import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -100,7 +101,7 @@ public void testSofaReferenceOnSingleParameterMethod() { Map properties = new HashMap<>(); properties.put("spring.application.name", "SofaServiceAndReferenceTest"); SpringApplication springApplication = new SpringApplication( - TestSofaReferenceOnMethodConfiguration.class, RuntimeConfiguration.class); + TestSofaServiceOnMethodConfiguration.class, RuntimeConfiguration.class); springApplication.setWebApplicationType(WebApplicationType.NONE); springApplication.setDefaultProperties(properties); ApplicationContext ctx = springApplication.run(); @@ -173,7 +174,7 @@ public void testSofaReferenceOnStaticField() throws IOException, NoSuchFieldExce properties.put("spring.application.name", "SofaServiceAndReferenceTest"); properties.put("logging.path", logRootPath); SpringApplication springApplication = new SpringApplication( - TestSofaReferenceOnMethodConfiguration.class, RuntimeConfiguration.class); + TestSofaServiceOnMethodConfiguration.class, RuntimeConfiguration.class); springApplication.setWebApplicationType(WebApplicationType.NONE); springApplication.setDefaultProperties(properties); springApplication.run(); @@ -186,6 +187,53 @@ public void testSofaReferenceOnStaticField() throws IOException, NoSuchFieldExce + SofaServiceAndReferenceTest.class.getDeclaredField("staticSampleService"))); } + @Test + public void testSofaServiceBeanLazyInit() throws IOException { + String logRootPath = StringUtils.hasText(System.getProperty("logging.path")) ? System + .getProperty("logging.path") : "./logs"; + File sofaLog = new File(logRootPath + File.separator + "sofa-runtime" + File.separator + + "sofa-default.log"); + FileUtils.write(sofaLog, "", System.getProperty("file.encoding")); + + Map properties = new HashMap<>(); + properties.put("spring.application.name", "SofaServiceAndReferenceTest"); + properties.put("logging.path", logRootPath); + SpringApplication springApplication = new SpringApplication( + TestSofaServiceConfiguration.class, RuntimeConfiguration.class); + springApplication.setLazyInitialization(true); + springApplication.setWebApplicationType(WebApplicationType.NONE); + springApplication.setDefaultProperties(properties); + ConfigurableApplicationContext context = springApplication.run(); + + String serviceBeanName = SofaBeanNameGenerator.generateSofaServiceBeanName( + SampleService.class, ""); + Assert.assertTrue(context.getBeanFactory().getBeanDefinition(serviceBeanName).isLazyInit()); + } + + @Test + public void testSofaReferenceBeanLazyInit() throws IOException { + String logRootPath = StringUtils.hasText(System.getProperty("logging.path")) ? System + .getProperty("logging.path") : "./logs"; + File sofaLog = new File(logRootPath + File.separator + "sofa-runtime" + File.separator + + "sofa-default.log"); + FileUtils.write(sofaLog, "", System.getProperty("file.encoding")); + + Map properties = new HashMap<>(); + properties.put("spring.application.name", "SofaServiceAndReferenceTest"); + properties.put("logging.path", logRootPath); + SpringApplication springApplication = new SpringApplication( + TestSofaReferenceOnMethodConfiguration.class, RuntimeConfiguration.class); + springApplication.setLazyInitialization(true); + springApplication.setWebApplicationType(WebApplicationType.NONE); + springApplication.setDefaultProperties(properties); + ConfigurableApplicationContext context = springApplication.run(); + + String referenceBeanName = SofaBeanNameGenerator.generateSofaReferenceBeanName( + SampleService.class, ""); + Assert.assertTrue(context.getBeanFactory().getBeanDefinition(referenceBeanName) + .isLazyInit()); + } + @Configuration(proxyBeanMethods = false) static class MultipleBindingsSofaServiceConfiguration { /** @@ -209,6 +257,14 @@ public SampleService sampleService(@SofaReference(uniqueId = "rpc", binding = @S @Configuration(proxyBeanMethods = false) static class TestSofaReferenceOnMethodConfiguration { + @Bean + public SampleService sampleService(@SofaReference SampleService sampleService) { + return new DefaultSampleService("TestSofaReferenceConfiguration"); + } + } + + @Configuration(proxyBeanMethods = false) + static class TestSofaServiceOnMethodConfiguration { @Bean public SofaServiceAndReferenceTest sofaServiceAndReferenceTest() { return new SofaServiceAndReferenceTest(); From 63186834f133826e66e05cea0a8e615c0feb1b9f Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Tue, 14 May 2024 10:34:00 +0800 Subject: [PATCH 31/47] Fix beans information lost parentId (#1320) Signed-off-by: JermaineHua --- .../actuator/beans/IsleBeansEndpoint.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) 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 70d046191..49ebb5de1 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 @@ -57,19 +57,33 @@ public ApplicationBeans beans() { ApplicationBeans applicationBeans = super.beans(); ApplicationRuntimeModel applicationRuntimeModel = context.getBean( SofaBootConstants.APPLICATION, ApplicationRuntimeModel.class); - Map moduleApplicationContexts = getModuleApplicationContexts(applicationRuntimeModel); + + String springParentId = null; + for (Map.Entry entry : applicationBeans.getContexts().entrySet()) { + if (entry.getValue().getParentId() == null) { + springParentId = entry.getKey(); + break; + } + } + + Map moduleApplicationContexts = getModuleApplicationContexts( + applicationRuntimeModel, springParentId); applicationBeans.getContexts().putAll(moduleApplicationContexts); return applicationBeans; } - 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) { ContextBeans contextBeans = describing((ConfigurableApplicationContext) applicationContext, - descriptor.getSpringParent()); + parentId); if (contextBeans != null) { contexts.put(descriptor.getModuleName(), contextBeans); } From 914b7faf43f643539513ac5a237b098197fae919 Mon Sep 17 00:00:00 2001 From: HzjNeverStop <441627022@qq.com> Date: Wed, 22 May 2024 14:27:08 +0800 Subject: [PATCH 32/47] Release 3.23.0 (#1322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * avoid concurrent problem in JvmFilterHolder * release 3.23.0 --------- Co-authored-by: 致节 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0b130abe1..a0accd6ae 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.23.0-SNAPSHOT + 3.23.0 ${revision} 1.6.7 From 62005e2dc59c7ad89224172de232567b906a62f0 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Fri, 2 Aug 2024 16:49:11 +0800 Subject: [PATCH 33/47] 3.24.0-SNAPSHOT (#1331) Signed-off-by: JermaineHua --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a0accd6ae..67ba06802 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.23.0 + 3.24.0-SNAPSHOT ${revision} 1.6.7 From f180dab2d8ac326c1ef8c5528d47e708f6ac2bb4 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Fri, 2 Aug 2024 16:49:38 +0800 Subject: [PATCH 34/47] Fix performance issues caused by debuglog (#1332) Signed-off-by: JermaineHua --- .../service/binding/JvmBindingAdapter.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/binding/JvmBindingAdapter.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/binding/JvmBindingAdapter.java index 999300996..cf3aa2949 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/binding/JvmBindingAdapter.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/service/binding/JvmBindingAdapter.java @@ -210,8 +210,10 @@ public Object doInvoke(MethodInvocation invocation) throws Throwable { + "] has already been destroyed."); } - SofaLogger.debug(">> Start in JVM service invoke, the service interface is - {}", - getInterfaceName()); + if (SofaLogger.isDebugEnabled()) { + SofaLogger.debug(">> Start in JVM service invoke, the service interface is - {}", + getInterfaceName()); + } Object retVal; Object targetObj = this.getTarget(); @@ -225,17 +227,21 @@ public Object doInvoke(MethodInvocation invocation) throws Throwable { try { return serviceProxy.invoke(invocation); } finally { - SofaLogger.debug( - "<< Finish Cross App JVM service invoke, the service is - {}]", - (getInterfaceName() + "#" + getUniqueId())); + if (SofaLogger.isDebugEnabled()) { + SofaLogger.debug( + "<< Finish Cross App JVM service invoke, the service is - {}]", + (getInterfaceName() + "#" + getUniqueId())); + } } } } if (targetObj == null || ((targetObj instanceof Proxy) && binding.hasBackupProxy())) { targetObj = binding.getBackupProxy(); - SofaLogger.debug("<<{}.{} backup proxy invoke.", getInterfaceName().getName(), - invocation.getMethod().getName()); + if (SofaLogger.isDebugEnabled()) { + SofaLogger.debug("<<{}.{} backup proxy invoke.", getInterfaceName().getName(), + invocation.getMethod().getName()); + } } if (targetObj == null) { @@ -250,9 +256,11 @@ public Object doInvoke(MethodInvocation invocation) throws Throwable { } catch (InvocationTargetException ex) { throw ex.getTargetException(); } finally { - SofaLogger.debug( - "<< Finish JVM service invoke, the service implementation is - {}]", - (this.target == null ? "null" : this.target.getClass().getName())); + if (SofaLogger.isDebugEnabled()) { + SofaLogger.debug( + "<< Finish JVM service invoke, the service implementation is - {}]", + (this.target == null ? "null" : this.target.getClass().getName())); + } popThreadContextClassLoader(tcl); } From 3073a9f86ad5a6df63277f660207c8080f1a22cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=B4=E8=8A=82?= Date: Tue, 13 Aug 2024 15:31:41 +0800 Subject: [PATCH 35/47] update nexus-staging-maven-plugin 1.7.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 67ba06802..767b3f85a 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ 3.24.0-SNAPSHOT ${revision} - 1.6.7 + 1.7.0 1.6 1.2.7 From 69c80736eb4d7a656a56f0c6f8e5c3a939b06764 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Mon, 19 Aug 2024 10:47:50 +0800 Subject: [PATCH 36/47] test deploy failed (#1336) Signed-off-by: JermaineHua --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b59968913..c7d43ffbe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Build with Maven - run: mvn --batch-mode deploy -DskipTests -Prelease + run: mvn --batch-mode deploy -DskipTests -Prelease -X env: MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} From 26395c9fcff1b35ef4d0d6551033cfa085ec48af Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Mon, 19 Aug 2024 14:00:41 +0800 Subject: [PATCH 37/47] Test deploy failed (#1338) * test deploy failed Signed-off-by: JermaineHua * Add mvn clean for release Signed-off-by: JermaineHua --------- Signed-off-by: JermaineHua --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7d43ffbe..8e9af87b9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Build with Maven - run: mvn --batch-mode deploy -DskipTests -Prelease -X + run: mvn clean install --batch-mode deploy -DskipTests -Prelease env: MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} From f6b6823b914d896416780350f660824460d899da Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Mon, 19 Aug 2024 14:12:35 +0800 Subject: [PATCH 38/47] Release 3.24.0 (#1337) Signed-off-by: JermaineHua --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 767b3f85a..6e2f23285 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.24.0-SNAPSHOT + 3.24.0 ${revision} 1.7.0 From 786d20472eb7ecafdfd139258dc2be9811b86316 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Mon, 23 Sep 2024 12:26:39 +0800 Subject: [PATCH 39/47] Upgrade hessian version to 3.5.5 (#1342) Signed-off-by: JermaineHua --- sofa-boot-project/sofaboot-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sofa-boot-project/sofaboot-dependencies/pom.xml b/sofa-boot-project/sofaboot-dependencies/pom.xml index e5b5e4904..f9f0eb2ff 100644 --- a/sofa-boot-project/sofaboot-dependencies/pom.xml +++ b/sofa-boot-project/sofaboot-dependencies/pom.xml @@ -31,7 +31,7 @@ 1.3.11 1.5.10 - 3.3.12 + 3.5.5 From c8e77a7c5146c7df16ae19ccc9171363e5385612 Mon Sep 17 00:00:00 2001 From: ropz <8840636+Jiiiiiin@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:55:01 +0800 Subject: [PATCH 40/47] Update SofaBootRpcStartListener.java (#1347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix 针对 上游 sofa-rpc-all 清理 Lookout https://github.com/sofastack/sofa-rpc/pull/1447 修复类缺失问题 --- .../rpc/boot/context/SofaBootRpcStartListener.java | 12 ------------ 1 file changed, 12 deletions(-) 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 663f0eff8..1402ac7db 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,7 +16,6 @@ */ 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.config.SofaBootRpcProperties; import com.alipay.sofa.rpc.boot.container.ProviderConfigContainer; @@ -24,7 +23,6 @@ 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; @@ -63,8 +61,6 @@ public SofaBootRpcStartListener(SofaBootRpcProperties sofaBootRpcProperties, @Override public void onApplicationEvent(SofaBootRpcStartEvent event) { - //choose disable metrics lookout - disableLookout(); //extra info processExtra(event); @@ -93,12 +89,4 @@ protected void processExtra(SofaBootRpcStartEvent event) { } - protected void disableLookout() { - Boolean disable = SofaBootRpcParserUtil.parseBoolean(sofaBootRpcProperties - .getLookoutCollectDisable()); - - if (disable != null) { - LookoutSubscriber.setLookoutCollectDisable(disable); - } - } } From 3aab05e35275e526b2239cd672edb082d60d9453 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Wed, 9 Apr 2025 16:46:34 +0800 Subject: [PATCH 41/47] manualReadinessCallbackMap remove classLoader when uninstall biz component (#1360) Signed-off-by: JermaineHua --- .../main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java index 8c215b13e..670e195b6 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/SofaRuntimeProperties.java @@ -136,6 +136,7 @@ public static void unRegisterProperties(ClassLoader classLoader) { skipAllComponentShutdownMap.remove(classLoader); skipCommonComponentShutdownMap.remove(classLoader); extensionFailureInsulatingMap.remove(classLoader); + manualReadinessCallbackMap.remove(classLoader); } public static Boolean isSkipJvmSerialize(ClassLoader classLoader) { From cb07d25d74b900b80abadf29c908993eeaa2d3f0 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Wed, 9 Apr 2025 18:13:20 +0800 Subject: [PATCH 42/47] Exception is thrown when component failed to register (#1361) * Exception is thrown when component failed to register Signed-off-by: JermaineHua * Fix testSofaServiceClassNotAssignableFromInterfaceType ut Signed-off-by: JermaineHua * Fix testServiceBinding Signed-off-by: JermaineHua * testBindingInstanceIsNotAssignableFromInterfaceType Signed-off-by: JermaineHua --------- Signed-off-by: JermaineHua --- .../sofa/runtime/component/impl/ComponentManagerImpl.java | 3 ++- .../runtime/test/AnnotationSofaServiceTypeCheckFailTest.java | 4 +++- .../java/com/alipay/sofa/runtime/test/ClientFactoryTest.java | 2 +- .../sofa/runtime/test/XMLSofaServiceTypeCheckFailTest.java | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java index 9dd8cf92e..d5c6ff90f 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java @@ -167,7 +167,7 @@ private ComponentInfo doRegister(ComponentInfo ci) { ci.register(); } catch (Throwable t) { SofaLogger.error(ErrorCode.convert("01-03003", ci.getName()), t); - return null; + throw new ServiceRuntimeException(ErrorCode.convert("01-03003", ci.getName())); } SofaLogger.info("Registering component: {}", ci.getName()); @@ -189,6 +189,7 @@ private ComponentInfo doRegister(ComponentInfo ci) { } catch (Throwable t) { ci.exception(new Exception(t)); SofaLogger.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/test/java/com/alipay/sofa/runtime/test/AnnotationSofaServiceTypeCheckFailTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/AnnotationSofaServiceTypeCheckFailTest.java index 013d6fa86..ff7d28d62 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/AnnotationSofaServiceTypeCheckFailTest.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/AnnotationSofaServiceTypeCheckFailTest.java @@ -24,6 +24,7 @@ import org.junit.AfterClass; import org.junit.Assert; import org.junit.Test; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -68,7 +69,8 @@ public void testSofaServiceClassNotAssignableFromInterfaceType() throws IOExcept TestSofaServiceNotAssignableFromInterfaceTypeConfiguration.class); springApplication.setWebApplicationType(WebApplicationType.NONE); springApplication.setDefaultProperties(properties); - springApplication.run(); + + Assert.assertThrows(BeanCreationException.class, springApplication::run); String content = FileUtils.readFileToString(sofaLog, System.getProperty("file.encoding")); Assert diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ClientFactoryTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ClientFactoryTest.java index cc18cff3f..4a22fec90 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ClientFactoryTest.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/ClientFactoryTest.java @@ -134,7 +134,7 @@ public void testBindingInstanceIsNotAssignableFromInterfaceType() throws IOExcep serviceParam.setInstance(new Object()); serviceParam.setInterfaceType(SampleService.class); serviceParam.addBindingParam(jvmBindingParam); - serviceClient.service(serviceParam); + Assert.assertThrows(Exception.class, ()-> serviceClient.service(serviceParam)); String content = FileUtils.readFileToString(sofaLog, System.getProperty("file.encoding")); Assert diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/XMLSofaServiceTypeCheckFailTest.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/XMLSofaServiceTypeCheckFailTest.java index e3c6e9b3b..af69e62ba 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/XMLSofaServiceTypeCheckFailTest.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/test/java/com/alipay/sofa/runtime/test/XMLSofaServiceTypeCheckFailTest.java @@ -66,7 +66,8 @@ public void testServiceBinding() throws IOException { SofaBindingTestIsNotAssignableFromInterfaceTypeConfiguration.class); springApplication.setWebApplicationType(WebApplicationType.NONE); springApplication.setDefaultProperties(properties); - springApplication.run(); + + Assert.assertThrows(Exception.class, springApplication::run); String content = FileUtils.readFileToString(sofaLog, System.getProperty("file.encoding")); Assert From 3bafa8768435a788a055b08bf54e115ebcacb02b Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Wed, 9 Apr 2025 18:14:30 +0800 Subject: [PATCH 43/47] Fix StandardSofaRuntimeManager mem leak (#1362) Signed-off-by: JermaineHua --- .../alipay/sofa/runtime/client/impl/ClientFactoryImpl.java | 5 +++++ .../runtime/component/impl/StandardSofaRuntimeManager.java | 2 ++ .../sofa/runtime/spi/client/ClientFactoryInternal.java | 2 ++ 3 files changed, 9 insertions(+) diff --git a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/client/impl/ClientFactoryImpl.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/client/impl/ClientFactoryImpl.java index 10861432e..5e9660074 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/client/impl/ClientFactoryImpl.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/client/impl/ClientFactoryImpl.java @@ -38,6 +38,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/component/impl/StandardSofaRuntimeManager.java b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/component/impl/StandardSofaRuntimeManager.java index ee3ffbff1..3b39dfffc 100644 --- a/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/component/impl/StandardSofaRuntimeManager.java +++ b/sofa-boot-project/sofa-boot-core/runtime-sofa-boot/src/main/java/com/alipay/sofa/runtime/component/impl/StandardSofaRuntimeManager.java @@ -129,11 +129,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 5bf9e09bd..fe8e68f57 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 @@ -31,4 +31,6 @@ public interface ClientFactoryInternal extends ClientFactory { * @param clientInstance client instance */ void registerClient(Class clientType, Object clientInstance); + + void destroy(); } From 204995201034aa693562b8640471288cf4e0cd14 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Wed, 9 Apr 2025 18:43:13 +0800 Subject: [PATCH 44/47] 3.25.0-SNAPSHOT (#1363) Signed-off-by: JermaineHua --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6e2f23285..ba2b37f3c 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.24.0 + 3.25.0-SNAPSHOT ${revision} 1.7.0 From cb8af937c469453ec5e4d452b5080f83aad4540a Mon Sep 17 00:00:00 2001 From: ropz <8840636+Jiiiiiin@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:40:40 +0800 Subject: [PATCH 45/47] Update pom.xml (#1348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix 针对 上游 sofa-rpc-all 清理 Lookout sofastack/sofa-rpc#1447 修复类缺失问题 --- sofa-boot-project/sofaboot-dependencies/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sofa-boot-project/sofaboot-dependencies/pom.xml b/sofa-boot-project/sofaboot-dependencies/pom.xml index f9f0eb2ff..08a5b8d34 100644 --- a/sofa-boot-project/sofaboot-dependencies/pom.xml +++ b/sofa-boot-project/sofaboot-dependencies/pom.xml @@ -26,11 +26,11 @@ 5.4.2 3.1.3 - 5.8.3 + 5.13.2 - 1.3.11 - 1.5.10 + 1.4.0 + 1.6.10 3.5.5 From d6bb8dbfbb954a8afbd7e1581ed6deecfc8490b1 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Tue, 13 May 2025 20:58:24 +0800 Subject: [PATCH 46/47] Fix build ci for ubuntu system version (#1369) Signed-off-by: JermaineHua --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 5657c7b94..e55440e2f 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -13,7 +13,7 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: jdk: [8, 11] From f6aaa6fd2cccfaca673bfa9147a00ad28d2592e6 Mon Sep 17 00:00:00 2001 From: Jermaine Hua Date: Thu, 29 May 2025 18:22:40 +0800 Subject: [PATCH 47/47] Release 3.25.0 (#1376) Signed-off-by: JermaineHua --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ba2b37f3c..af6571797 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ SOFABoot Build - 3.25.0-SNAPSHOT + 3.25.0 ${revision} 1.7.0