From 74a24b76a71aeebd7d3e3cc5c17d6a9bd8a1c4a0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:18 +0800 Subject: [PATCH 001/310] Update maven-build.yml --- .github/workflows/maven-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index df4fe003..3f4981a5 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -10,17 +10,17 @@ name: Maven Build on: push: - branches: [ 'dev-1.x' ] + branches: [ 'dev' ] pull_request: - branches: [ 'dev-1.x' , 'release-1.x' ] + branches: [ 'main', 'dev' , 'release' ] jobs: build: runs-on: ubuntu-latest strategy: matrix: - java: [ '8', '11' , '17' , '21' ] - maven-profile-spring-cloud: [ 'spring-cloud-hoxton' , 'spring-cloud-2020' , 'spring-cloud-2021' ] + java: [ '17' , '21' ] + maven-profile-spring-cloud: [ 'spring-cloud-2022' , 'spring-cloud-2023' , 'spring-cloud-2024' ] steps: - name: Checkout Source uses: actions/checkout@v4 From 282028c52d01784d7b568cbc8a74e17df6192688 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:19 +0800 Subject: [PATCH 002/310] Update maven-publish.yml --- .github/workflows/maven-publish.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 241727bb..41170da0 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -10,13 +10,13 @@ name: Maven Publish on: push: - branches: [ 'release-1.x' ] + branches: [ 'release' ] workflow_dispatch: inputs: revision: - description: 'The version to publish' + description: 'The version to publish for Spring Cloud 2022+ and JDK 17+' required: true - default: '0.0.1-SNAPSHOT' + default: '2.0.0-SNAPSHOT' jobs: build: @@ -29,7 +29,7 @@ jobs: - name: Setup Maven Central Repository uses: actions/setup-java@v4 with: - java-version: '11' + java-version: '17' distribution: 'temurin' server-id: ossrh server-username: MAVEN_USERNAME From ec48345fed353661057bffbd9fc10ff15e610d70 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:23 +0800 Subject: [PATCH 003/310] Update WebMvcServiceRegistryAutoConfiguration.java --- .../autoconfigure/WebMvcServiceRegistryAutoConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index deb59f3c..df17c7ad 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -21,6 +21,7 @@ import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; import io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent; import io.microsphere.spring.web.metadata.WebEndpointMapping; +import jakarta.servlet.Filter; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -35,7 +36,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; -import javax.servlet.Filter; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; From 482aaf55a0428e226238bfb2430958cb9a60b2ed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:27 +0800 Subject: [PATCH 004/310] Update TomcatDynamicConfigurationListener.java --- .../tomcat/event/TomcatDynamicConfigurationListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java index 712ca8b1..21974029 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java @@ -213,8 +213,8 @@ private void configureHttp11Protocol(ServerProperties refreshableServerPropertie // Max HTTP Header Size configure("Tomcat HTTP Headers' max size(bytes)") - .value(refreshableServerProperties::getMaxHttpHeaderSize) - .compare(currentServerProperties::getMaxHttpHeaderSize) + .value(refreshableServerProperties::getMaxHttpRequestHeaderSize) + .compare(currentServerProperties::getMaxHttpRequestHeaderSize) .as(this::toIntBytes) .on(this::isPositive) .apply(protocol::setMaxHttpHeaderSize); From c525e506e5da879a87e8e8b37967bd6dc4c378ed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:30 +0800 Subject: [PATCH 005/310] Update spring.factories --- .../src/main/resources/META-INF/spring.factories | 9 --------- 1 file changed, 9 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring.factories b/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring.factories index a0de4297..7586f87c 100644 --- a/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring.factories +++ b/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring.factories @@ -1,10 +1 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -io.microsphere.spring.cloud.client.discovery.autoconfigure.DiscoveryClientAutoConfiguration,\ -io.microsphere.spring.cloud.client.service.registry.autoconfigure.ServiceRegistryAutoConfiguration,\ -io.microsphere.spring.cloud.client.service.registry.autoconfigure.WebMvcServiceRegistryAutoConfiguration,\ -io.microsphere.spring.cloud.client.service.registry.autoconfigure.WebFluxServiceRegistryAutoConfiguration,\ -io.microsphere.spring.cloud.client.service.registry.autoconfigure.SimpleAutoServiceRegistrationAutoConfiguration,\ -io.microsphere.spring.cloud.client.service.registry.actuate.autoconfigure.ServiceRegistrationEndpointAutoConfiguration,\ -io.microsphere.spring.cloud.fault.tolerance.tomcat.autoconfigure.TomcatFaultToleranceAutoConfiguration - com.alibaba.cloud.nacos.registry.NacosServiceRegistry = com.alibaba.cloud.nacos.registry.NacosRegistration \ No newline at end of file From cf3c0f24ba8f7e444c150698d1ba842442fd73d6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:32 +0800 Subject: [PATCH 006/310] Update org.springframework.boot.autoconfigure.AutoConfiguration.imports --- ...pringframework.boot.autoconfigure.AutoConfiguration.imports | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 7be8d06f..55c28f69 100644 --- a/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -3,4 +3,5 @@ io.microsphere.spring.cloud.client.service.registry.autoconfigure.ServiceRegistr io.microsphere.spring.cloud.client.service.registry.autoconfigure.WebMvcServiceRegistryAutoConfiguration io.microsphere.spring.cloud.client.service.registry.autoconfigure.WebFluxServiceRegistryAutoConfiguration io.microsphere.spring.cloud.client.service.registry.autoconfigure.SimpleAutoServiceRegistrationAutoConfiguration -io.microsphere.spring.cloud.client.service.registry.actuate.autoconfigure.ServiceRegistrationEndpointAutoConfiguration \ No newline at end of file +io.microsphere.spring.cloud.client.service.registry.actuate.autoconfigure.ServiceRegistrationEndpointAutoConfiguration +io.microsphere.spring.cloud.fault.tolerance.tomcat.autoconfigure.TomcatFaultToleranceAutoConfiguration \ No newline at end of file From 065630e32dff08fbe345f152bb281b13e3bd7a63 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:35 +0800 Subject: [PATCH 007/310] Update TomcatFaultToleranceAutoConfigurationTest.java --- .../TomcatFaultToleranceAutoConfigurationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java index 320338a0..945f5833 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java @@ -112,7 +112,7 @@ public void testMaxHttpHeaderSize() { // default assertEquals(8192, protocol.getMaxHttpHeaderSize()); // changed - environmentManager.setProperty("server.max-http-header-size", "5120"); + environmentManager.setProperty("server.max-http-request-header-size", "5120"); assertEquals(5120, protocol.getMaxHttpHeaderSize()); } From 44a2138fcd2a399833550ec4308180b3962c8d33 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:41 +0800 Subject: [PATCH 008/310] Update application.yaml --- .../src/test/resources/application.yaml | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/resources/application.yaml b/microsphere-spring-cloud-commons/src/test/resources/application.yaml index db6965c0..943d64ad 100644 --- a/microsphere-spring-cloud-commons/src/test/resources/application.yaml +++ b/microsphere-spring-cloud-commons/src/test/resources/application.yaml @@ -29,7 +29,9 @@ eureka: --- spring: - profiles: nacos + config: + activate: + on-profile: nacos cloud: nacos: @@ -44,7 +46,9 @@ spring: --- spring: - profiles: eureka + config: + activate: + on-profile: eureka eureka: client: @@ -55,7 +59,9 @@ eureka: --- spring: - profiles: zookeeper + config: + activate: + on-profile: zookeeper cloud: zookeeper: enabled: true @@ -64,7 +70,9 @@ spring: --- spring: - profiles: consul + config: + activate: + on-profile: consul cloud: consul: @@ -75,7 +83,9 @@ spring: --- spring: - profiles: kubernetes + config: + activate: + on-profile: kubernetes cloud: kubernetes: From 2b0e7c1a40ea6e1fd802e5dfe4b575822870fbea Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:45 +0800 Subject: [PATCH 009/310] Update pom.xml --- microsphere-spring-cloud-parent/pom.xml | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 58aafe55..1dbea262 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -20,8 +20,8 @@ - 0.1.1 - 1.20.1 + 0.2.1 + 1.20.4 @@ -78,32 +78,32 @@ - spring-cloud-hoxton + spring-cloud-2022 - 2.3.12.RELEASE - Hoxton.SR12 - 2.2.10 + 3.0.13 + 2022.0.5 + 2022.0.0.2 - spring-cloud-2020 + spring-cloud-2023 - 2.4.13 - 2020.0.5 - 2021.1 + 3.2.12 + 2023.0.5 + 2023.0.3.2 - spring-cloud-2021 + spring-cloud-2024 true - 2.6.15 - 2021.0.9 - 2021.0.6.1 + 3.4.0 + 2024.0.0 + 2023.0.3.2 From 218d8b589e89dddeafb97cb5ca17da66b9233001 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:04:49 +0800 Subject: [PATCH 010/310] Update pom.xml --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5788ef7..21106849 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,8 @@ - 0.1.1-SNAPSHOT + 2.0.0-SNAPSHOT + 17 From 02d4a6386405db52d2e36e8226b53a3a2a522c7d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 19:13:58 +0800 Subject: [PATCH 011/310] Polish Bugfix --- .../autorefresh/AutoRefreshCapability.java | 20 +++++++++---------- .../components/DecoratedContract.java | 6 +++--- .../components/DecoratedDecoder.java | 6 +++--- .../components/DecoratedEncoder.java | 6 +++--- .../components/DecoratedErrorDecoder.java | 6 +++--- .../components/DecoratedFeignComponent.java | 20 +++++++++---------- .../components/DecoratedQueryMapEncoder.java | 6 +++--- .../components/DecoratedRetryer.java | 6 +++--- ...t.autoconfigure.AutoConfiguration.imports} | 1 - 9 files changed, 38 insertions(+), 39 deletions(-) rename microsphere-spring-cloud-openfeign/src/main/resources/META-INF/{spring.factories => org.springframework.boot.autoconfigure.AutoConfiguration.imports} (58%) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 0e29d0b2..599ea4d9 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -16,8 +16,8 @@ import io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder; import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; import org.springframework.beans.BeansException; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignContext; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -28,16 +28,16 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAware { private final FeignComponentRegistry componentRegistry; - private final FeignContext feignContext; + private final FeignClientFactory feignClientFactory; private final FeignClientProperties clientProperties; private String contextId; private static final String CONTEXT_ID_PROPERTY_NAME = "feign.client.name"; - public AutoRefreshCapability(FeignClientProperties clientProperties, FeignContext feignContext, FeignComponentRegistry componentRegistry) { + public AutoRefreshCapability(FeignClientProperties clientProperties, FeignClientFactory feignClientFactory, FeignComponentRegistry componentRegistry) { this.clientProperties = clientProperties; - this.feignContext = feignContext; + this.feignClientFactory = feignClientFactory; this.componentRegistry = componentRegistry; } @@ -53,7 +53,7 @@ public Retryer enrich(Retryer retryer) { return null; DecoratedRetryer decoratedRetryer = DecoratedFeignComponent.instantiate(DecoratedRetryer.class, Retryer.class, - contextId, feignContext, clientProperties, retryer); + contextId, feignClientFactory, clientProperties, retryer); this.componentRegistry.register(contextId, decoratedRetryer); return decoratedRetryer; @@ -65,7 +65,7 @@ public Contract enrich(Contract contract) { return null; DecoratedContract decoratedContract = DecoratedFeignComponent.instantiate(DecoratedContract.class, Contract.class, - contextId, feignContext, clientProperties, contract); + contextId, feignClientFactory, clientProperties, contract); this.componentRegistry.register(contextId, decoratedContract); return decoratedContract; } @@ -76,7 +76,7 @@ public Decoder enrich(Decoder decoder) { return null; DecoratedDecoder decoratedDecoder = DecoratedFeignComponent.instantiate(DecoratedDecoder.class, Decoder.class, - contextId, feignContext, clientProperties, decoder); + contextId, feignClientFactory, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedDecoder); return decoratedDecoder; } @@ -87,7 +87,7 @@ public Encoder enrich(Encoder encoder) { return null; DecoratedEncoder decoratedEncoder = DecoratedFeignComponent.instantiate(DecoratedEncoder.class, Encoder.class, - contextId, feignContext, clientProperties, encoder); + contextId, feignClientFactory, clientProperties, encoder); this.componentRegistry.register(contextId, decoratedEncoder); return decoratedEncoder; } @@ -97,7 +97,7 @@ public ErrorDecoder enrich(ErrorDecoder decoder) { return null; DecoratedErrorDecoder decoratedErrorDecoder = DecoratedFeignComponent.instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, - contextId, feignContext, clientProperties, decoder); + contextId, feignClientFactory, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedErrorDecoder); return decoratedErrorDecoder; } @@ -113,7 +113,7 @@ public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { return null; DecoratedQueryMapEncoder decoratedQueryMapEncoder = DecoratedFeignComponent.instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, - contextId, feignContext, clientProperties, queryMapEncoder); + contextId, feignClientFactory, clientProperties, queryMapEncoder); this.componentRegistry.register(contextId, decoratedQueryMapEncoder); return decoratedQueryMapEncoder; diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index a49185c5..2f489a03 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -2,8 +2,8 @@ import feign.Contract; import feign.MethodMetadata; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignContext; import java.util.List; @@ -13,8 +13,8 @@ */ public class DecoratedContract extends DecoratedFeignComponent implements Contract { - public DecoratedContract(String contextId, FeignContext feignContext, FeignClientProperties clientProperties, Contract delegate) { - super(contextId, feignContext, clientProperties, delegate); + public DecoratedContract(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Contract delegate) { + super(contextId, feignClientFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index 0734aaa4..1da204c1 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -4,8 +4,8 @@ import feign.Response; import feign.codec.DecodeException; import feign.codec.Decoder; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignContext; import java.io.IOException; import java.lang.reflect.Type; @@ -16,8 +16,8 @@ */ public class DecoratedDecoder extends DecoratedFeignComponent implements Decoder { - public DecoratedDecoder(String contextId, FeignContext feignContext, FeignClientProperties clientProperties, Decoder delegate) { - super(contextId, feignContext, clientProperties, delegate); + public DecoratedDecoder(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Decoder delegate) { + super(contextId, feignClientFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index dfd37050..28256306 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -3,8 +3,8 @@ import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignContext; import java.lang.reflect.Type; @@ -14,8 +14,8 @@ */ public class DecoratedEncoder extends DecoratedFeignComponent implements Encoder { - public DecoratedEncoder(String contextId, FeignContext feignContext, FeignClientProperties clientProperties, Encoder delegate) { - super(contextId, feignContext, clientProperties, delegate); + public DecoratedEncoder(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Encoder delegate) { + super(contextId, feignClientFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 5173bbb7..326daf5d 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -2,8 +2,8 @@ import feign.Response; import feign.codec.ErrorDecoder; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignContext; /** * @author 韩超 @@ -11,8 +11,8 @@ */ public class DecoratedErrorDecoder extends DecoratedFeignComponent implements ErrorDecoder { - public DecoratedErrorDecoder(String contextId, FeignContext feignContext, FeignClientProperties clientProperties, ErrorDecoder delegate) { - super(contextId, feignContext, clientProperties, delegate); + public DecoratedErrorDecoder(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, ErrorDecoder delegate) { + super(contextId, feignClientFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 79884de3..0303098d 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -4,8 +4,8 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignContext; import org.springframework.lang.NonNull; import java.lang.reflect.Constructor; @@ -19,7 +19,7 @@ public abstract class DecoratedFeignComponent implements Refreshable { private final Logger log = LoggerFactory.getLogger(getClass()); - private final FeignContext feignContext; + private final FeignClientFactory feignClientFactory; private final String contextId; private final FeignClientProperties clientProperties; @@ -32,9 +32,9 @@ public abstract class DecoratedFeignComponent implements Refreshable { private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); - public DecoratedFeignComponent(String contextId, FeignContext feignContext, FeignClientProperties clientProperties, T delegate) { + public DecoratedFeignComponent(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, T delegate) { this.contextId = contextId; - this.feignContext = feignContext; + this.feignClientFactory = feignClientFactory; this.clientProperties = clientProperties; this.delegate = delegate; } @@ -52,8 +52,8 @@ public T delegate() { } @NonNull - public FeignContext getFeignContext() { - return this.feignContext; + public FeignClientFactory getFeignClientFactory() { + return this.feignClientFactory; } @NonNull @@ -83,7 +83,7 @@ protected T loadInstance() { String contextId = contextId(); writeLock.lock(); try { - T component = getFeignContext().getInstance(contextId, componentType); + T component = getFeignClientFactory().getInstance(contextId, componentType); if (component == null) component = BeanUtils.instantiateClass(componentType); this.delegate = component; @@ -112,10 +112,10 @@ public String toString() { } public static , T> W instantiate(Class decoratedClass, Class componentClass, - String contextId, FeignContext feignContext, FeignClientProperties clientProperties, T delegate) { + String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, T delegate) { try { - Constructor constructor = decoratedClass.getConstructor(String.class, FeignContext.class, FeignClientProperties.class, componentClass); - return BeanUtils.instantiateClass(constructor, contextId, feignContext, clientProperties, delegate); + Constructor constructor = decoratedClass.getConstructor(String.class, FeignClientFactory.class, FeignClientProperties.class, componentClass); + return BeanUtils.instantiateClass(constructor, contextId, feignClientFactory, clientProperties, delegate); } catch (NoSuchMethodException noSuchMethodException) { throw new BeanInstantiationException(decoratedClass, noSuchMethodException.getLocalizedMessage()); } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index d66f9323..8bf0d756 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -2,8 +2,8 @@ import feign.QueryMapEncoder; import io.microsphere.logging.Logger; +import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignContext; import java.lang.invoke.MethodHandle; import java.util.Map; @@ -23,8 +23,8 @@ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent韩超 @@ -11,8 +11,8 @@ */ public class DecoratedRetryer extends DecoratedFeignComponent implements Retryer { - public DecoratedRetryer(String contextId, FeignContext feignContext, FeignClientProperties clientProperties, Retryer delegate) { - super(contextId, feignContext, clientProperties, delegate); + public DecoratedRetryer(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Retryer delegate) { + super(contextId, feignClientFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/spring.factories b/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 58% rename from microsphere-spring-cloud-openfeign/src/main/resources/META-INF/spring.factories rename to microsphere-spring-cloud-openfeign/src/main/resources/META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 896056cc..6284a91b 100644 --- a/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/spring.factories +++ b/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ io.microsphere.spring.cloud.openfeign.autoconfigure.FeignClientAutoRefreshAutoConfiguration \ No newline at end of file From ef68ad149292d75d657fdfba76e37fceb42e160a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 21:05:43 +0800 Subject: [PATCH 012/310] Fixes actions --- .github/workflows/maven-build.yml | 2 +- README.md | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 3f4981a5..024a6783 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -45,4 +45,4 @@ jobs: uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - slug: microsphere-projects/microsphere-spring-boot \ No newline at end of file + slug: microsphere-projects/microsphere-spring-cloud \ No newline at end of file diff --git a/README.md b/README.md index e2791caa..6e350487 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ -# microsphere-spring-cloud +# Microsphere Spring Cloud + Microsphere Projects for Spring Cloud + +[![Maven Build](https://github.com/microsphere-projects/microsphere-spring-cloud/actions/workflows/maven-build.yml/badge.svg)](https://github.com/microsphere-projects/microsphere-spring-cloud/actions/workflows/maven-build.yml) +[![Codecov](https://codecov.io/gh/microsphere-projects/microsphere-spring-cloud/branch/dev/graph/badge.svg)](https://app.codecov.io/gh/microsphere-projects/microsphere-spring-cloud) +![Maven](https://img.shields.io/maven-central/v/io.github.microsphere-projects/microsphere-spring-cloud.svg) +![License](https://img.shields.io/github/license/microsphere-projects/microsphere-spring-cloud.svg) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/microsphere-projects/microsphere-spring-cloud.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-cloud "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/microsphere-projects/microsphere-spring-cloud.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-cloud "Percentage of issues still open") + From 81b6cee515a083b060cc468f28581b2204e2a535 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Jan 2025 21:08:59 +0800 Subject: [PATCH 013/310] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e350487..52c087a3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Microsphere Projects for Spring Cloud [![Maven Build](https://github.com/microsphere-projects/microsphere-spring-cloud/actions/workflows/maven-build.yml/badge.svg)](https://github.com/microsphere-projects/microsphere-spring-cloud/actions/workflows/maven-build.yml) -[![Codecov](https://codecov.io/gh/microsphere-projects/microsphere-spring-cloud/branch/dev/graph/badge.svg)](https://app.codecov.io/gh/microsphere-projects/microsphere-spring-cloud) +[![Codecov](https://codecov.io/gh/microsphere-projects/microsphere-spring-cloud/branch/main/graph/badge.svg)](https://app.codecov.io/gh/microsphere-projects/microsphere-spring-cloud) ![Maven](https://img.shields.io/maven-central/v/io.github.microsphere-projects/microsphere-spring-cloud.svg) ![License](https://img.shields.io/github/license/microsphere-projects/microsphere-spring-cloud.svg) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/microsphere-projects/microsphere-spring-cloud.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-cloud "Average time to resolve an issue") From 8c389eb873522886e2c207ee6f5a6e571d5a8b91 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 16 Jan 2025 18:46:51 +0800 Subject: [PATCH 014/310] Update EnableFeignAutoRefresh.java --- .../cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java index 1587b120..7433fb07 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java @@ -19,7 +19,7 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented -@Import(FeignClientAutoRefreshAutoConfiguration.class) +@Import(EnableFeignAutoRefresh.Marker.class) public @interface EnableFeignAutoRefresh { class Marker { From 5b8d97bb49d6ef0e78aae3c6d07f41a3bcab5406 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 16 Jan 2025 18:57:02 +0800 Subject: [PATCH 015/310] Change the Spring properties for testing --- .../io/microsphere/spring/cloud/openfeign/BaseTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java index 7197e845..49f7734c 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java @@ -30,10 +30,10 @@ */ @TestPropertySource(properties = { "spring.main.allow-bean-definition-overriding=true", - "feign.client.config.default.encoder=io.microsphere.spring.cloud.openfeign.encoder.AEncoder", - "feign.client.config.default.request-interceptors[0]=io.microsphere.spring.cloud.openfeign.requestInterceptor.ARequestInterceptor", - "feign.client.config.default.default-request-headers.app=my-app", - "feign.client.config.default.default-query-parameters.sign=my-sign", + "spring.cloud.openfeign.client.config.default.encoder=io.microsphere.spring.cloud.openfeign.encoder.AEncoder", + "spring.cloud.openfeign.client.config.default.request-interceptors[0]=io.microsphere.spring.cloud.openfeign.requestInterceptor.ARequestInterceptor", + "spring.cloud.openfeign.client.config.default.default-request-headers.app=my-app", + "spring.cloud.openfeign.client.config.default.default-query-parameters.sign=my-sign", }) @ComponentScan(basePackages = "io.microsphere.spring.cloud.openfeign") @EnableFeignClients(clients = BaseClient.class) From ae858d3741c6688ff38a9ff241af1f615493b822 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 16 Jan 2025 20:57:59 +0800 Subject: [PATCH 016/310] Change the property name prefixes --- .../cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java | 2 ++ .../cloud/openfeign/autorefresh/AutoRefreshCapability.java | 2 +- .../autorefresh/FeignClientConfigurationChangedListener.java | 3 +-- ...pringframework.boot.autoconfigure.AutoConfiguration.imports | 0 .../spring/cloud/openfeign/encoder/EncoderChangedTest.java | 2 +- .../cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java | 2 +- .../openfeign/querymapencoder/QueryMapEncoderChangedTest.java | 2 +- .../requestInterceptor/RequestInterceptorChangedTest.java | 2 +- .../spring/cloud/openfeign/retryer/RetryerChangedTest.java | 2 +- 9 files changed, 9 insertions(+), 8 deletions(-) rename microsphere-spring-cloud-openfeign/src/main/resources/META-INF/{ => spring}/org.springframework.boot.autoconfigure.AutoConfiguration.imports (100%) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java index 7433fb07..da5ab512 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java @@ -4,6 +4,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -19,6 +20,7 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented +@Inherited @Import(EnableFeignAutoRefresh.Marker.class) public @interface EnableFeignAutoRefresh { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 599ea4d9..495ee28c 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -33,7 +33,7 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAwar private String contextId; - private static final String CONTEXT_ID_PROPERTY_NAME = "feign.client.name"; + private static final String CONTEXT_ID_PROPERTY_NAME = "spring.cloud.openfeign.client.name"; public AutoRefreshCapability(FeignClientProperties clientProperties, FeignClientFactory feignClientFactory, FeignComponentRegistry componentRegistry) { this.clientProperties = clientProperties; diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java index c4cede8c..b6858265 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java @@ -19,13 +19,12 @@ public FeignClientConfigurationChangedListener(FeignComponentRegistry registry) this.registry = registry; } - private final String PREFIX = "feign.client.config."; + private final String PREFIX = "spring.cloud.openfeign.client.config."; @Override public void onApplicationEvent(EnvironmentChangeEvent event) { Map> effectiveClients = resolveChangedClient(event); effectiveClients.forEach(registry::refresh); - } protected Map> resolveChangedClient(EnvironmentChangeEvent event) { diff --git a/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 100% rename from microsphere-spring-cloud-openfeign/src/main/resources/META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename to microsphere-spring-cloud-openfeign/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java index 938ea9fb..7989641c 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java @@ -15,7 +15,7 @@ public class EncoderChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.encoder"; + return "spring.cloud.openfeign.client.config.my-client.encoder"; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java index e431c1f4..79a2e1b4 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java @@ -15,7 +15,7 @@ public class ErrorDecoderChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.error-decoder"; + return "spring.cloud.openfeign.client.config.my-client.error-decoder"; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java index 69928a64..14f9ba8f 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java @@ -15,7 +15,7 @@ public class QueryMapEncoderChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.query-map-encoder"; + return "spring.cloud.openfeign.client.config.my-client.query-map-encoder"; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java index 870b94a6..0b47980a 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java @@ -18,7 +18,7 @@ public class RequestInterceptorChangedTest extends BaseTest @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.request-interceptors[0]"; + return "spring.cloud.openfeign.client.config.my-client.request-interceptors[0]"; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java index 5c66d255..5139dfc6 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java @@ -15,7 +15,7 @@ public class RetryerChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.retryer"; + return "spring.cloud.openfeign.client.config.my-client.retryer"; } @Override From 7f01fdbfe5f287c28e33860f8f59feb47812ecea Mon Sep 17 00:00:00 2001 From: Kurok1 Date: Thu, 16 Jan 2025 21:11:25 +0800 Subject: [PATCH 017/310] allow feign load component from main application-context --- .../openfeign/FeignComponentProvider.java | 37 ++++++++++++++++++ ...ignClientAutoRefreshAutoConfiguration.java | 10 ++++- .../autorefresh/AutoRefreshCapability.java | 39 +++++++------------ ...ignClientConfigurationChangedListener.java | 4 +- .../components/DecoratedContract.java | 6 +-- .../components/DecoratedDecoder.java | 6 +-- .../components/DecoratedEncoder.java | 6 +-- .../components/DecoratedErrorDecoder.java | 6 +-- .../components/DecoratedFeignComponent.java | 23 +++++------ .../components/DecoratedQueryMapEncoder.java | 6 +-- .../components/DecoratedRetryer.java | 6 +-- ...ot.autoconfigure.AutoConfiguration.imports | 0 .../spring/cloud/openfeign/BaseTest.java | 5 --- .../RequestInterceptorChangedTest.java | 5 +-- 14 files changed, 94 insertions(+), 65 deletions(-) create mode 100644 microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java rename microsphere-spring-cloud-openfeign/src/main/resources/META-INF/{ => spring}/org.springframework.boot.autoconfigure.AutoConfiguration.imports (100%) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java new file mode 100644 index 00000000..3b204d59 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java @@ -0,0 +1,37 @@ +package io.microsphere.spring.cloud.openfeign; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.cloud.context.named.NamedContextFactory; +import org.springframework.cloud.openfeign.FeignClientSpecification; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class FeignComponentProvider implements BeanFactoryAware { + + private final NamedContextFactory contextFactory; + + private BeanFactory mainBeanFactory; + + public FeignComponentProvider(NamedContextFactory contextFactory) { + this.contextFactory = contextFactory; + } + + public T getInstance(String contextName, Class type) { + T component = this.contextFactory.getInstance(contextName, type); + if (component == null && mainBeanFactory instanceof AutowireCapableBeanFactory) { + //create new + return ((AutowireCapableBeanFactory)this.mainBeanFactory).createBean(type); + } + return component; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.mainBeanFactory = beanFactory; + } +} diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java index 93c5e366..d6c8fbba 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java @@ -1,5 +1,6 @@ package io.microsphere.spring.cloud.openfeign.autoconfigure; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import io.microsphere.spring.cloud.openfeign.autorefresh.FeignClientConfigurationChangedListener; import io.microsphere.spring.cloud.openfeign.autorefresh.FeignComponentRegistry; import io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor; @@ -7,8 +8,10 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignBuilderCustomizer; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; import org.springframework.context.annotation.Bean; /** @@ -20,7 +23,7 @@ * @since 0.0.1 */ @ConditionalOnBean(EnableFeignAutoRefresh.Marker.class) -@AutoConfigureAfter(ConfigurationPropertiesRebinderAutoConfiguration.class) +//@AutoConfigureAfter(ConfigurationPropertiesRebinderAutoConfiguration.class) public class FeignClientAutoRefreshAutoConfiguration { @Bean @@ -45,4 +48,9 @@ public FeignClientSpecificationPostProcessor feignClientSpecificationPostProcess return new FeignClientSpecificationPostProcessor(); } + @Bean + public FeignComponentProvider feignComponentProvider(NamedContextFactory contextFactory) { + return new FeignComponentProvider(contextFactory); + } + } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 599ea4d9..f18ace67 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -1,22 +1,12 @@ package io.microsphere.spring.cloud.openfeign.autorefresh; -import feign.Capability; -import feign.Contract; -import feign.QueryMapEncoder; -import feign.RequestInterceptor; -import feign.Retryer; +import feign.*; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedContract; -import io.microsphere.spring.cloud.openfeign.components.DecoratedDecoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedEncoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedErrorDecoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent; -import io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; +import io.microsphere.spring.cloud.openfeign.components.*; import org.springframework.beans.BeansException; -import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -28,22 +18,21 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAware { private final FeignComponentRegistry componentRegistry; - private final FeignClientFactory feignClientFactory; + //private final FeignClientFactory feignClientFactory; + private final FeignComponentProvider componentProvider; private final FeignClientProperties clientProperties; private String contextId; - private static final String CONTEXT_ID_PROPERTY_NAME = "feign.client.name"; - - public AutoRefreshCapability(FeignClientProperties clientProperties, FeignClientFactory feignClientFactory, FeignComponentRegistry componentRegistry) { + public AutoRefreshCapability(FeignClientProperties clientProperties, FeignComponentProvider componentProvider, FeignComponentRegistry componentRegistry) { this.clientProperties = clientProperties; - this.feignClientFactory = feignClientFactory; + this.componentProvider = componentProvider; this.componentRegistry = componentRegistry; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.contextId = applicationContext.getEnvironment().getProperty(CONTEXT_ID_PROPERTY_NAME); + this.contextId = applicationContext.getEnvironment().getProperty("spring.cloud.openfeign.client.name"); } @@ -53,7 +42,7 @@ public Retryer enrich(Retryer retryer) { return null; DecoratedRetryer decoratedRetryer = DecoratedFeignComponent.instantiate(DecoratedRetryer.class, Retryer.class, - contextId, feignClientFactory, clientProperties, retryer); + contextId, componentProvider, clientProperties, retryer); this.componentRegistry.register(contextId, decoratedRetryer); return decoratedRetryer; @@ -65,7 +54,7 @@ public Contract enrich(Contract contract) { return null; DecoratedContract decoratedContract = DecoratedFeignComponent.instantiate(DecoratedContract.class, Contract.class, - contextId, feignClientFactory, clientProperties, contract); + contextId, componentProvider, clientProperties, contract); this.componentRegistry.register(contextId, decoratedContract); return decoratedContract; } @@ -76,7 +65,7 @@ public Decoder enrich(Decoder decoder) { return null; DecoratedDecoder decoratedDecoder = DecoratedFeignComponent.instantiate(DecoratedDecoder.class, Decoder.class, - contextId, feignClientFactory, clientProperties, decoder); + contextId, componentProvider, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedDecoder); return decoratedDecoder; } @@ -87,7 +76,7 @@ public Encoder enrich(Encoder encoder) { return null; DecoratedEncoder decoratedEncoder = DecoratedFeignComponent.instantiate(DecoratedEncoder.class, Encoder.class, - contextId, feignClientFactory, clientProperties, encoder); + contextId, componentProvider, clientProperties, encoder); this.componentRegistry.register(contextId, decoratedEncoder); return decoratedEncoder; } @@ -97,7 +86,7 @@ public ErrorDecoder enrich(ErrorDecoder decoder) { return null; DecoratedErrorDecoder decoratedErrorDecoder = DecoratedFeignComponent.instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, - contextId, feignClientFactory, clientProperties, decoder); + contextId, componentProvider, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedErrorDecoder); return decoratedErrorDecoder; } @@ -113,7 +102,7 @@ public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { return null; DecoratedQueryMapEncoder decoratedQueryMapEncoder = DecoratedFeignComponent.instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, - contextId, feignClientFactory, clientProperties, queryMapEncoder); + contextId, componentProvider, clientProperties, queryMapEncoder); this.componentRegistry.register(contextId, decoratedQueryMapEncoder); return decoratedQueryMapEncoder; diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java index c4cede8c..b4d8ed26 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java @@ -19,7 +19,9 @@ public FeignClientConfigurationChangedListener(FeignComponentRegistry registry) this.registry = registry; } - private final String PREFIX = "feign.client.config."; + private final String PREFIX = "spring.cloud.openfeign.client.config."; + + @Override public void onApplicationEvent(EnvironmentChangeEvent event) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index 2f489a03..05eee3db 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -2,7 +2,7 @@ import feign.Contract; import feign.MethodMetadata; -import org.springframework.cloud.openfeign.FeignClientFactory; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import org.springframework.cloud.openfeign.FeignClientProperties; import java.util.List; @@ -13,8 +13,8 @@ */ public class DecoratedContract extends DecoratedFeignComponent implements Contract { - public DecoratedContract(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Contract delegate) { - super(contextId, feignClientFactory, clientProperties, delegate); + public DecoratedContract(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Contract delegate) { + super(contextId, feignComponentProvider, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index 1da204c1..91da63df 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -4,7 +4,7 @@ import feign.Response; import feign.codec.DecodeException; import feign.codec.Decoder; -import org.springframework.cloud.openfeign.FeignClientFactory; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import org.springframework.cloud.openfeign.FeignClientProperties; import java.io.IOException; @@ -16,8 +16,8 @@ */ public class DecoratedDecoder extends DecoratedFeignComponent implements Decoder { - public DecoratedDecoder(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Decoder delegate) { - super(contextId, feignClientFactory, clientProperties, delegate); + public DecoratedDecoder(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Decoder delegate) { + super(contextId, feignComponentProvider, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index 28256306..3ef7a3ba 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -3,7 +3,7 @@ import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; -import org.springframework.cloud.openfeign.FeignClientFactory; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import org.springframework.cloud.openfeign.FeignClientProperties; import java.lang.reflect.Type; @@ -14,8 +14,8 @@ */ public class DecoratedEncoder extends DecoratedFeignComponent implements Encoder { - public DecoratedEncoder(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Encoder delegate) { - super(contextId, feignClientFactory, clientProperties, delegate); + public DecoratedEncoder(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Encoder delegate) { + super(contextId, feignComponentProvider, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 326daf5d..9876660e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -2,7 +2,7 @@ import feign.Response; import feign.codec.ErrorDecoder; -import org.springframework.cloud.openfeign.FeignClientFactory; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import org.springframework.cloud.openfeign.FeignClientProperties; /** @@ -11,8 +11,8 @@ */ public class DecoratedErrorDecoder extends DecoratedFeignComponent implements ErrorDecoder { - public DecoratedErrorDecoder(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, ErrorDecoder delegate) { - super(contextId, feignClientFactory, clientProperties, delegate); + public DecoratedErrorDecoder(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, ErrorDecoder delegate) { + super(contextId, feignComponentProvider, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 0303098d..aeb65b63 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -1,10 +1,10 @@ package io.microsphere.spring.cloud.openfeign.components; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; -import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.lang.NonNull; @@ -19,7 +19,8 @@ public abstract class DecoratedFeignComponent implements Refreshable { private final Logger log = LoggerFactory.getLogger(getClass()); - private final FeignClientFactory feignClientFactory; + //private final FeignClientFactory feignClientFactory; + private final FeignComponentProvider componentProvider; private final String contextId; private final FeignClientProperties clientProperties; @@ -32,9 +33,9 @@ public abstract class DecoratedFeignComponent implements Refreshable { private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); - public DecoratedFeignComponent(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, T delegate) { + public DecoratedFeignComponent(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, T delegate) { this.contextId = contextId; - this.feignClientFactory = feignClientFactory; + this.componentProvider = feignComponentProvider; this.clientProperties = clientProperties; this.delegate = delegate; } @@ -52,9 +53,9 @@ public T delegate() { } @NonNull - public FeignClientFactory getFeignClientFactory() { - return this.feignClientFactory; - } + public FeignComponentProvider getFeignComponentProvider() { + return this.componentProvider; + }; @NonNull public String contextId() { @@ -83,7 +84,7 @@ protected T loadInstance() { String contextId = contextId(); writeLock.lock(); try { - T component = getFeignClientFactory().getInstance(contextId, componentType); + T component = getFeignComponentProvider().getInstance(contextId, componentType); if (component == null) component = BeanUtils.instantiateClass(componentType); this.delegate = component; @@ -112,10 +113,10 @@ public String toString() { } public static , T> W instantiate(Class decoratedClass, Class componentClass, - String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, T delegate) { + String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, T delegate) { try { - Constructor constructor = decoratedClass.getConstructor(String.class, FeignClientFactory.class, FeignClientProperties.class, componentClass); - return BeanUtils.instantiateClass(constructor, contextId, feignClientFactory, clientProperties, delegate); + Constructor constructor = decoratedClass.getConstructor(String.class, FeignComponentProvider.class, FeignClientProperties.class, componentClass); + return BeanUtils.instantiateClass(constructor, contextId, feignComponentProvider, clientProperties, delegate); } catch (NoSuchMethodException noSuchMethodException) { throw new BeanInstantiationException(decoratedClass, noSuchMethodException.getLocalizedMessage()); } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index 8bf0d756..6e4d941e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -2,7 +2,7 @@ import feign.QueryMapEncoder; import io.microsphere.logging.Logger; -import org.springframework.cloud.openfeign.FeignClientFactory; +import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import org.springframework.cloud.openfeign.FeignClientProperties; import java.lang.invoke.MethodHandle; @@ -23,8 +23,8 @@ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent implements Retryer { - public DecoratedRetryer(String contextId, FeignClientFactory feignClientFactory, FeignClientProperties clientProperties, Retryer delegate) { - super(contextId, feignClientFactory, clientProperties, delegate); + public DecoratedRetryer(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Retryer delegate) { + super(contextId, feignComponentProvider, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/microsphere-spring-cloud-openfeign/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 100% rename from microsphere-spring-cloud-openfeign/src/main/resources/META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename to microsphere-spring-cloud-openfeign/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java index 49f7734c..c21b1f61 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java @@ -5,9 +5,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.endpoint.event.RefreshEvent; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -38,7 +35,6 @@ @ComponentScan(basePackages = "io.microsphere.spring.cloud.openfeign") @EnableFeignClients(clients = BaseClient.class) @EnableFeignAutoRefresh -@AutoConfigureAfter(ConfigurationPropertiesRebinderAutoConfiguration.class) public abstract class BaseTest { private static final Logger log = LoggerFactory.getLogger(BaseTest.class); @@ -73,7 +69,6 @@ public void replaceConfig() { public void testInternal() { try { this.client.echo("hello", "1.0"); - } catch (Exception ignored) { } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java index 870b94a6..f9651f15 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java @@ -2,11 +2,8 @@ import feign.RequestInterceptor; import io.microsphere.spring.cloud.openfeign.BaseTest; -import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; /** * @author 韩超 @@ -18,7 +15,7 @@ public class RequestInterceptorChangedTest extends BaseTest @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.request-interceptors[0]"; + return "spring.cloud.openfeign.client.config.my-client.request-interceptors[0]"; } @Override From 4986f5086802360104d0ef91f0d3886433c2cf9c Mon Sep 17 00:00:00 2001 From: Kurok1 Date: Thu, 16 Jan 2025 21:11:48 +0800 Subject: [PATCH 018/310] test config change --- .../spring/cloud/openfeign/encoder/EncoderChangedTest.java | 2 +- .../cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java | 2 +- .../openfeign/querymapencoder/QueryMapEncoderChangedTest.java | 2 +- .../spring/cloud/openfeign/retryer/RetryerChangedTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java index 938ea9fb..7989641c 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java @@ -15,7 +15,7 @@ public class EncoderChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.encoder"; + return "spring.cloud.openfeign.client.config.my-client.encoder"; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java index e431c1f4..79a2e1b4 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java @@ -15,7 +15,7 @@ public class ErrorDecoderChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.error-decoder"; + return "spring.cloud.openfeign.client.config.my-client.error-decoder"; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java index 69928a64..14f9ba8f 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java @@ -15,7 +15,7 @@ public class QueryMapEncoderChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.query-map-encoder"; + return "spring.cloud.openfeign.client.config.my-client.query-map-encoder"; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java index 5c66d255..5139dfc6 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java @@ -15,7 +15,7 @@ public class RetryerChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { - return "feign.client.config.my-client.retryer"; + return "spring.cloud.openfeign.client.config.my-client.retryer"; } @Override From 931dfa2d7bf92d9ca310208378ee33afc74089e4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 16 Jan 2025 21:15:30 +0800 Subject: [PATCH 019/310] Bugfix --- ...ignClientAutoRefreshAutoConfiguration.java | 21 +++++++++++++++---- ...ignClientConfigurationChangedListener.java | 4 +++- .../spring/cloud/openfeign/BaseTest.java | 1 - 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java index 93c5e366..2116a41e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java @@ -5,11 +5,16 @@ import io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor; import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; import org.springframework.cloud.openfeign.FeignBuilderCustomizer; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; /** * The Auto-Configuration class for {@link EnableFeignAutoRefresh} @@ -20,7 +25,6 @@ * @since 0.0.1 */ @ConditionalOnBean(EnableFeignAutoRefresh.Marker.class) -@AutoConfigureAfter(ConfigurationPropertiesRebinderAutoConfiguration.class) public class FeignClientAutoRefreshAutoConfiguration { @Bean @@ -30,9 +34,12 @@ public FeignBuilderCustomizer addDefaultRequestInterceptorCustomizer() { }; } - @Bean - public FeignClientConfigurationChangedListener feignClientConfigurationChangedListener(FeignComponentRegistry registry) { - return new FeignClientConfigurationChangedListener(registry); + @EventListener(ApplicationReadyEvent.class) + public void onApplicationReadyEvent(ApplicationReadyEvent event) { + /** + * Make sure the FeignClientConfigurationChangedListener is registered after the ConfigurationPropertiesRebinder + */ + registerFeignClientConfigurationChangedListener(event); } @Bean @@ -45,4 +52,10 @@ public FeignClientSpecificationPostProcessor feignClientSpecificationPostProcess return new FeignClientSpecificationPostProcessor(); } + private void registerFeignClientConfigurationChangedListener(ApplicationReadyEvent event) { + ConfigurableApplicationContext context = event.getApplicationContext(); + FeignComponentRegistry feignComponentRegistry = context.getBean(FeignComponentRegistry.class); + context.addApplicationListener(new FeignClientConfigurationChangedListener(feignComponentRegistry)); + } + } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java index b6858265..835245b8 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java @@ -24,7 +24,9 @@ public FeignClientConfigurationChangedListener(FeignComponentRegistry registry) @Override public void onApplicationEvent(EnvironmentChangeEvent event) { Map> effectiveClients = resolveChangedClient(event); - effectiveClients.forEach(registry::refresh); + if (!effectiveClients.isEmpty()) { + effectiveClients.forEach(registry::refresh); + } } protected Map> resolveChangedClient(EnvironmentChangeEvent event) { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java index 49f7734c..72c6c140 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java @@ -38,7 +38,6 @@ @ComponentScan(basePackages = "io.microsphere.spring.cloud.openfeign") @EnableFeignClients(clients = BaseClient.class) @EnableFeignAutoRefresh -@AutoConfigureAfter(ConfigurationPropertiesRebinderAutoConfiguration.class) public abstract class BaseTest { private static final Logger log = LoggerFactory.getLogger(BaseTest.class); From cbace64ca733dc43717ab83039362b28c0136a64 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 16 Jan 2025 21:31:31 +0800 Subject: [PATCH 020/310] Reimport --- .../FeignClientAutoRefreshAutoConfiguration.java | 5 ----- .../autorefresh/AutoRefreshCapability.java | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java index b1a5182e..bc01cd16 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java @@ -5,18 +5,14 @@ import io.microsphere.spring.cloud.openfeign.autorefresh.FeignComponentRegistry; import io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor; import org.springframework.beans.factory.BeanFactory; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignBuilderCustomizer; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientSpecification; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; -import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; /** @@ -28,7 +24,6 @@ * @since 0.0.1 */ @ConditionalOnBean(EnableFeignAutoRefresh.Marker.class) -//@AutoConfigureAfter(ConfigurationPropertiesRebinderAutoConfiguration.class) public class FeignClientAutoRefreshAutoConfiguration { @Bean diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index ae865fff..884c00a0 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -1,11 +1,21 @@ package io.microsphere.spring.cloud.openfeign.autorefresh; -import feign.*; +import feign.Capability; +import feign.Contract; +import feign.QueryMapEncoder; +import feign.RequestInterceptor; +import feign.Retryer; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; -import io.microsphere.spring.cloud.openfeign.components.*; +import io.microsphere.spring.cloud.openfeign.components.DecoratedContract; +import io.microsphere.spring.cloud.openfeign.components.DecoratedDecoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedEncoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedErrorDecoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent; +import io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; import org.springframework.beans.BeansException; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.context.ApplicationContext; From 80a834e56aa4f0c97e4a12e62168b24c6c83120f Mon Sep 17 00:00:00 2001 From: Kurok1 Date: Fri, 17 Jan 2025 11:18:03 +0800 Subject: [PATCH 021/310] change test suit --- .../openfeign/FeignComponentProvider.java | 37 ------- ...ignClientAutoRefreshAutoConfiguration.java | 7 -- .../autorefresh/AutoRefreshCapability.java | 35 +++---- .../CompositedRequestInterceptor.java | 10 +- .../components/DecoratedContract.java | 7 +- .../components/DecoratedDecoder.java | 7 +- .../components/DecoratedEncoder.java | 7 +- .../components/DecoratedErrorDecoder.java | 7 +- .../components/DecoratedFeignComponent.java | 28 +++--- .../components/DecoratedQueryMapEncoder.java | 7 +- .../components/DecoratedRetryer.java | 7 +- .../spring/cloud/openfeign/BaseClient.java | 2 +- .../spring/cloud/openfeign/BaseTest.java | 16 ++- .../cloud/openfeign/FeignComponentAssert.java | 20 ++++ .../cloud/openfeign/MockCapability.java | 18 ++++ .../ObservableFeignInvocationHandler.java | 97 +++++++++++++++++++ .../cloud/openfeign/decoder/ADecoder.java | 21 ++++ .../cloud/openfeign/decoder/BDecoder.java | 21 ++++ .../openfeign/decoder/DecoderChangedTest.java | 36 +++++++ .../decoder/DecoderComponentAssert.java | 25 +++++ .../cloud/openfeign/encoder/AEncoder.java | 1 - .../cloud/openfeign/encoder/BEncoder.java | 1 - .../openfeign/encoder/EncoderChangedTest.java | 12 +++ .../encoder/EncoderComponentAssert.java | 35 +++++++ .../openfeign/errordecoder/AErrorDecoder.java | 1 - .../openfeign/errordecoder/BErrorEncoder.java | 16 +++ .../errordecoder/ErrorDecoderChangedTest.java | 13 ++- .../ErrorDecoderComponentAssert.java | 25 +++++ .../querymapencoder/AQueryMapEncoder.java | 1 - .../querymapencoder/BQueryMapEncoder.java | 1 - .../QueryMapEncoderChangedTest.java | 11 +++ .../QueryMapEncoderComponentAssert.java | 29 ++++++ .../ARequestInterceptor.java | 1 - .../BRequestInterceptor.java | 1 - .../RequestInterceptorChangedTest.java | 11 +++ .../RequestInterceptorComponentAssert.java | 42 ++++++++ .../cloud/openfeign/retryer/BRetry.java | 21 ++++ .../openfeign/retryer/RetryerChangedTest.java | 13 ++- .../retryer/RetryerComponentAssert.java | 25 +++++ 39 files changed, 560 insertions(+), 115 deletions(-) delete mode 100644 microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/MockCapability.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/ADecoder.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/BDecoder.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/BErrorEncoder.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/BRetry.java create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java deleted file mode 100644 index 3b204d59..00000000 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/FeignComponentProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.microsphere.spring.cloud.openfeign; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.cloud.context.named.NamedContextFactory; -import org.springframework.cloud.openfeign.FeignClientSpecification; - -/** - * @author 韩超 - * @since 0.0.1 - */ -public class FeignComponentProvider implements BeanFactoryAware { - - private final NamedContextFactory contextFactory; - - private BeanFactory mainBeanFactory; - - public FeignComponentProvider(NamedContextFactory contextFactory) { - this.contextFactory = contextFactory; - } - - public T getInstance(String contextName, Class type) { - T component = this.contextFactory.getInstance(contextName, type); - if (component == null && mainBeanFactory instanceof AutowireCapableBeanFactory) { - //create new - return ((AutowireCapableBeanFactory)this.mainBeanFactory).createBean(type); - } - return component; - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.mainBeanFactory = beanFactory; - } -} diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java index bc01cd16..6eacb45c 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.openfeign.autoconfigure; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import io.microsphere.spring.cloud.openfeign.autorefresh.FeignClientConfigurationChangedListener; import io.microsphere.spring.cloud.openfeign.autorefresh.FeignComponentRegistry; import io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor; @@ -10,7 +9,6 @@ import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignBuilderCustomizer; import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.cloud.openfeign.FeignClientSpecification; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.event.EventListener; @@ -51,11 +49,6 @@ public FeignClientSpecificationPostProcessor feignClientSpecificationPostProcess return new FeignClientSpecificationPostProcessor(); } - @Bean - public FeignComponentProvider feignComponentProvider(NamedContextFactory contextFactory) { - return new FeignComponentProvider(contextFactory); - } - private void registerFeignClientConfigurationChangedListener(ApplicationReadyEvent event) { ConfigurableApplicationContext context = event.getApplicationContext(); FeignComponentRegistry feignComponentRegistry = context.getBean(FeignComponentRegistry.class); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 884c00a0..9fe6808b 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -1,23 +1,14 @@ package io.microsphere.spring.cloud.openfeign.autorefresh; -import feign.Capability; -import feign.Contract; -import feign.QueryMapEncoder; -import feign.RequestInterceptor; -import feign.Retryer; +import feign.*; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; -import io.microsphere.spring.cloud.openfeign.components.DecoratedContract; -import io.microsphere.spring.cloud.openfeign.components.DecoratedDecoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedEncoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedErrorDecoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent; -import io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; +import io.microsphere.spring.cloud.openfeign.components.*; import org.springframework.beans.BeansException; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -29,15 +20,15 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAwar private final FeignComponentRegistry componentRegistry; //private final FeignClientFactory feignClientFactory; - private final FeignComponentProvider componentProvider; + private final NamedContextFactory contextFactory; private final FeignClientProperties clientProperties; private String contextId; - public AutoRefreshCapability(FeignClientProperties clientProperties, FeignComponentProvider componentProvider, FeignComponentRegistry componentRegistry) { + public AutoRefreshCapability(FeignClientProperties clientProperties, NamedContextFactory contextFactory, FeignComponentRegistry componentRegistry) { this.clientProperties = clientProperties; - this.componentProvider = componentProvider; + this.contextFactory = contextFactory; this.componentRegistry = componentRegistry; } @@ -53,7 +44,7 @@ public Retryer enrich(Retryer retryer) { return null; DecoratedRetryer decoratedRetryer = DecoratedFeignComponent.instantiate(DecoratedRetryer.class, Retryer.class, - contextId, componentProvider, clientProperties, retryer); + contextId, contextFactory, clientProperties, retryer); this.componentRegistry.register(contextId, decoratedRetryer); return decoratedRetryer; @@ -65,7 +56,7 @@ public Contract enrich(Contract contract) { return null; DecoratedContract decoratedContract = DecoratedFeignComponent.instantiate(DecoratedContract.class, Contract.class, - contextId, componentProvider, clientProperties, contract); + contextId, contextFactory, clientProperties, contract); this.componentRegistry.register(contextId, decoratedContract); return decoratedContract; } @@ -76,7 +67,7 @@ public Decoder enrich(Decoder decoder) { return null; DecoratedDecoder decoratedDecoder = DecoratedFeignComponent.instantiate(DecoratedDecoder.class, Decoder.class, - contextId, componentProvider, clientProperties, decoder); + contextId, contextFactory, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedDecoder); return decoratedDecoder; } @@ -87,7 +78,7 @@ public Encoder enrich(Encoder encoder) { return null; DecoratedEncoder decoratedEncoder = DecoratedFeignComponent.instantiate(DecoratedEncoder.class, Encoder.class, - contextId, componentProvider, clientProperties, encoder); + contextId, contextFactory, clientProperties, encoder); this.componentRegistry.register(contextId, decoratedEncoder); return decoratedEncoder; } @@ -97,7 +88,7 @@ public ErrorDecoder enrich(ErrorDecoder decoder) { return null; DecoratedErrorDecoder decoratedErrorDecoder = DecoratedFeignComponent.instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, - contextId, componentProvider, clientProperties, decoder); + contextId, contextFactory, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedErrorDecoder); return decoratedErrorDecoder; } @@ -113,7 +104,7 @@ public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { return null; DecoratedQueryMapEncoder decoratedQueryMapEncoder = DecoratedFeignComponent.instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, - contextId, componentProvider, clientProperties, queryMapEncoder); + contextId, contextFactory, clientProperties, queryMapEncoder); this.componentRegistry.register(contextId, decoratedQueryMapEncoder); return decoratedQueryMapEncoder; diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index 4cb3cf9f..d7237a9f 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -7,11 +7,7 @@ import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.util.CollectionUtils; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author 韩超 @@ -29,6 +25,10 @@ public CompositedRequestInterceptor(String contextId, BeanFactory beanFactory) { this.contextId = contextId; } + public Set getRequestInterceptors() { + return Collections.unmodifiableSet(set); + } + @Override public void apply(RequestTemplate template) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index 05eee3db..025ef5f2 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -2,8 +2,9 @@ import feign.Contract; import feign.MethodMetadata; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; import java.util.List; @@ -13,8 +14,8 @@ */ public class DecoratedContract extends DecoratedFeignComponent implements Contract { - public DecoratedContract(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Contract delegate) { - super(contextId, feignComponentProvider, clientProperties, delegate); + public DecoratedContract(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Contract delegate) { + super(contextId, contextFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index 91da63df..afcb0aee 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -4,8 +4,9 @@ import feign.Response; import feign.codec.DecodeException; import feign.codec.Decoder; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; import java.io.IOException; import java.lang.reflect.Type; @@ -16,8 +17,8 @@ */ public class DecoratedDecoder extends DecoratedFeignComponent implements Decoder { - public DecoratedDecoder(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Decoder delegate) { - super(contextId, feignComponentProvider, clientProperties, delegate); + public DecoratedDecoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Decoder delegate) { + super(contextId, contextFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index 3ef7a3ba..951e6ca3 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -3,8 +3,9 @@ import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; import java.lang.reflect.Type; @@ -14,8 +15,8 @@ */ public class DecoratedEncoder extends DecoratedFeignComponent implements Encoder { - public DecoratedEncoder(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Encoder delegate) { - super(contextId, feignComponentProvider, clientProperties, delegate); + public DecoratedEncoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Encoder delegate) { + super(contextId, contextFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 9876660e..c9c97d7a 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -2,8 +2,9 @@ import feign.Response; import feign.codec.ErrorDecoder; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; /** * @author 韩超 @@ -11,8 +12,8 @@ */ public class DecoratedErrorDecoder extends DecoratedFeignComponent implements ErrorDecoder { - public DecoratedErrorDecoder(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, ErrorDecoder delegate) { - super(contextId, feignComponentProvider, clientProperties, delegate); + public DecoratedErrorDecoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, ErrorDecoder delegate) { + super(contextId, contextFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index aeb65b63..0995f2ca 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -1,11 +1,12 @@ package io.microsphere.spring.cloud.openfeign.components; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; import org.springframework.lang.NonNull; import java.lang.reflect.Constructor; @@ -20,7 +21,7 @@ public abstract class DecoratedFeignComponent implements Refreshable { private final Logger log = LoggerFactory.getLogger(getClass()); //private final FeignClientFactory feignClientFactory; - private final FeignComponentProvider componentProvider; + private final NamedContextFactory contextFactory; private final String contextId; private final FeignClientProperties clientProperties; @@ -33,9 +34,9 @@ public abstract class DecoratedFeignComponent implements Refreshable { private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); - public DecoratedFeignComponent(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, T delegate) { + public DecoratedFeignComponent(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { this.contextId = contextId; - this.componentProvider = feignComponentProvider; + this.contextFactory = contextFactory; this.clientProperties = clientProperties; this.delegate = delegate; } @@ -53,9 +54,12 @@ public T delegate() { } @NonNull - public FeignComponentProvider getFeignComponentProvider() { - return this.componentProvider; - }; + public T loadInstanceFromContextFactory(String contextId, Class componentType) { + T component = this.contextFactory.getInstance(contextId, componentType); + if (component == null) + return this.contextFactory.getParent().getAutowireCapableBeanFactory().createBean(componentType); + return component; + } @NonNull public String contextId() { @@ -84,9 +88,7 @@ protected T loadInstance() { String contextId = contextId(); writeLock.lock(); try { - T component = getFeignComponentProvider().getInstance(contextId, componentType); - if (component == null) - component = BeanUtils.instantiateClass(componentType); + T component = loadInstanceFromContextFactory(contextId, componentType); this.delegate = component; return component; } catch (Throwable ex) { @@ -113,10 +115,10 @@ public String toString() { } public static , T> W instantiate(Class decoratedClass, Class componentClass, - String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, T delegate) { + String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { try { - Constructor constructor = decoratedClass.getConstructor(String.class, FeignComponentProvider.class, FeignClientProperties.class, componentClass); - return BeanUtils.instantiateClass(constructor, contextId, feignComponentProvider, clientProperties, delegate); + Constructor constructor = decoratedClass.getConstructor(String.class, NamedContextFactory.class, FeignClientProperties.class, componentClass); + return BeanUtils.instantiateClass(constructor, contextId, contextFactory, clientProperties, delegate); } catch (NoSuchMethodException noSuchMethodException) { throw new BeanInstantiationException(decoratedClass, noSuchMethodException.getLocalizedMessage()); } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index 6e4d941e..c40c1400 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -2,8 +2,9 @@ import feign.QueryMapEncoder; import io.microsphere.logging.Logger; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; import java.lang.invoke.MethodHandle; import java.util.Map; @@ -23,8 +24,8 @@ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent contextFactory, FeignClientProperties clientProperties, QueryMapEncoder delegate) { + super(contextId, contextFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java index 1e5dd598..3d1cb2c3 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java @@ -2,8 +2,9 @@ import feign.RetryableException; import feign.Retryer; -import io.microsphere.spring.cloud.openfeign.FeignComponentProvider; +import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; /** * @author 韩超 @@ -11,8 +12,8 @@ */ public class DecoratedRetryer extends DecoratedFeignComponent implements Retryer { - public DecoratedRetryer(String contextId, FeignComponentProvider feignComponentProvider, FeignClientProperties clientProperties, Retryer delegate) { - super(contextId, feignComponentProvider, clientProperties, delegate); + public DecoratedRetryer(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Retryer delegate) { + super(contextId, contextFactory, clientProperties, delegate); } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseClient.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseClient.java index 3a4edcf0..47de7f4d 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseClient.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseClient.java @@ -9,7 +9,7 @@ * @author 韩超 * @since 0.0.1 */ -@FeignClient(contextId = "my-client", name = "my-client") +@FeignClient(contextId = "my-client", name = "my-client", configuration = {MockCapability.class}) public interface BaseClient { @GetMapping("echo") diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java index c21b1f61..6f7a3d78 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java @@ -1,6 +1,8 @@ package io.microsphere.spring.cloud.openfeign; import io.microsphere.spring.cloud.openfeign.autoconfigure.EnableFeignAutoRefresh; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +30,10 @@ @TestPropertySource(properties = { "spring.main.allow-bean-definition-overriding=true", "spring.cloud.openfeign.client.config.default.encoder=io.microsphere.spring.cloud.openfeign.encoder.AEncoder", + "spring.cloud.openfeign.client.config.default.error-decoder=io.microsphere.spring.cloud.openfeign.errordecoder.AErrorDecoder", + "spring.cloud.openfeign.client.config.default.query-map-encoder=io.microsphere.spring.cloud.openfeign.querymapencoder.AQueryMapEncoder", + "spring.cloud.openfeign.client.config.default.retryer=io.microsphere.spring.cloud.openfeign.retryer.ARetry", + "spring.cloud.openfeign.client.config.default.decoder=io.microsphere.spring.cloud.openfeign.decoder.ADecoder", "spring.cloud.openfeign.client.config.default.request-interceptors[0]=io.microsphere.spring.cloud.openfeign.requestInterceptor.ARequestInterceptor", "spring.cloud.openfeign.client.config.default.default-request-headers.app=my-app", "spring.cloud.openfeign.client.config.default.default-query-parameters.sign=my-sign", @@ -46,7 +52,9 @@ public abstract class BaseTest { private BaseClient client; protected abstract String afterTestComponentConfigKey(); + protected abstract Class beforeTestComponentClass(); protected abstract Class afterTestComponent(); + protected abstract FeignComponentAssert loadFeignComponentAssert(); public void replaceConfig() { final String key = afterTestComponentConfigKey(); @@ -67,19 +75,21 @@ public void replaceConfig() { @Test public void testInternal() { + ObservableFeignInvocationHandler.componentAssert = loadFeignComponentAssert(); + + ObservableFeignInvocationHandler.expectComponentClass = beforeTestComponentClass(); try { this.client.echo("hello", "1.0"); } catch (Exception ignored) { - } replaceConfig(); + + ObservableFeignInvocationHandler.expectComponentClass = afterTestComponent(); try { this.client.echo("world", "1.0"); } catch (Exception ignored) { } - - } protected void triggerRefreshEvent() { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java new file mode 100644 index 00000000..485239ce --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java @@ -0,0 +1,20 @@ +package io.microsphere.spring.cloud.openfeign; + +import feign.MethodHandlerConfiguration; +import feign.ResponseHandler; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public abstract class FeignComponentAssert { + + + protected abstract T loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception; + + public boolean expect(MethodHandlerConfiguration configuration, ResponseHandler responseHandler, Class expectedClass) throws Exception { + T component = loadCurrentComponent(configuration, responseHandler); + return expectedClass.equals(component.getClass()); + } + +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/MockCapability.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/MockCapability.java new file mode 100644 index 00000000..53562b77 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/MockCapability.java @@ -0,0 +1,18 @@ +package io.microsphere.spring.cloud.openfeign; + +import feign.Capability; +import feign.InvocationHandlerFactory; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class MockCapability implements Capability { + + @Override + public InvocationHandlerFactory enrich(InvocationHandlerFactory invocationHandlerFactory) { + return ObservableFeignInvocationHandler::new; + } + + +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java new file mode 100644 index 00000000..6fdbae80 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java @@ -0,0 +1,97 @@ +package io.microsphere.spring.cloud.openfeign; + +import feign.InvocationHandlerFactory; +import feign.MethodHandlerConfiguration; +import feign.ResponseHandler; +import feign.Target; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Map; + +import static feign.Util.checkNotNull; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class ObservableFeignInvocationHandler implements InvocationHandler { + + + private static final Logger log = LoggerFactory.getLogger(ObservableFeignInvocationHandler.class); + public static FeignComponentAssert componentAssert; + public static Class expectComponentClass; + + private final Target target; + private final Map dispatch; + + ObservableFeignInvocationHandler(Target target, Map dispatch) { + this.target = checkNotNull(target, "target"); + this.dispatch = checkNotNull(dispatch, "dispatch for %s", target); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("equals".equals(method.getName())) { + try { + Object otherHandler = + args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null; + return equals(otherHandler); + } catch (IllegalArgumentException e) { + return false; + } + } else if ("hashCode".equals(method.getName())) { + return hashCode(); + } else if ("toString".equals(method.getName())) { + return toString(); + } else if (!dispatch.containsKey(method)) { + throw new UnsupportedOperationException( + String.format("Method \"%s\" should not be called", method.getName())); + } + + InvocationHandlerFactory.MethodHandler methodHandler = dispatch.get(method); + MethodHandlerConfiguration methodHandlerConfiguration = loadMethodHandlerConfiguration(methodHandler); + ResponseHandler responseHandler = loadResponseHandler(methodHandler); + Assert.isTrue(componentAssert.expect(methodHandlerConfiguration, responseHandler, expectComponentClass), "unexpected component"); + log.trace("component validation is True"); + return dispatch.get(method).invoke(args); + } + + protected MethodHandlerConfiguration loadMethodHandlerConfiguration(InvocationHandlerFactory.MethodHandler methodHandler) throws Exception { + Class configurationType = methodHandler.getClass(); + Field field = configurationType.getDeclaredField("methodHandlerConfiguration"); + field.setAccessible(true); + return (MethodHandlerConfiguration) field.get(methodHandler); + } + + protected ResponseHandler loadResponseHandler(InvocationHandlerFactory.MethodHandler methodHandler) throws Exception { + Class configurationType = methodHandler.getClass(); + Field field = configurationType.getDeclaredField("responseHandler"); + field.setAccessible(true); + return (ResponseHandler) field.get(methodHandler); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ObservableFeignInvocationHandler) { + ObservableFeignInvocationHandler other = (ObservableFeignInvocationHandler) obj; + return target.equals(other.target); + } + return false; + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public String toString() { + return target.toString(); + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/ADecoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/ADecoder.java new file mode 100644 index 00000000..14b85353 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/ADecoder.java @@ -0,0 +1,21 @@ +package io.microsphere.spring.cloud.openfeign.decoder; + +import feign.FeignException; +import feign.Response; +import feign.codec.DecodeException; +import feign.codec.Decoder; + +import java.io.IOException; +import java.lang.reflect.Type; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class ADecoder implements Decoder { + + @Override + public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { + return null; + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/BDecoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/BDecoder.java new file mode 100644 index 00000000..7762fe7d --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/BDecoder.java @@ -0,0 +1,21 @@ +package io.microsphere.spring.cloud.openfeign.decoder; + +import feign.FeignException; +import feign.Response; +import feign.codec.DecodeException; +import feign.codec.Decoder; + +import java.io.IOException; +import java.lang.reflect.Type; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class BDecoder implements Decoder { + + @Override + public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { + return null; + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java new file mode 100644 index 00000000..bbbb810c --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java @@ -0,0 +1,36 @@ +package io.microsphere.spring.cloud.openfeign.decoder; + +import feign.codec.Decoder; +import io.microsphere.spring.cloud.openfeign.BaseTest; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * @author 韩超 + * @since 0.0.1 + */ +@SpringBootTest(classes = DecoderChangedTest.class) +@EnableAutoConfiguration +public class DecoderChangedTest extends BaseTest { + + @Override + protected String afterTestComponentConfigKey() { + return "spring.cloud.openfeign.client.config.my-client.decoder"; + } + + @Override + protected Class beforeTestComponentClass() { + return ADecoder.class; + } + + @Override + protected Class afterTestComponent() { + return BDecoder.class; + } + + @Override + protected FeignComponentAssert loadFeignComponentAssert() { + return new DecoderComponentAssert(); + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java new file mode 100644 index 00000000..2502f4de --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java @@ -0,0 +1,25 @@ +package io.microsphere.spring.cloud.openfeign.decoder; + +import feign.MethodHandlerConfiguration; +import feign.ResponseHandler; +import feign.codec.Decoder; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; +import io.microsphere.spring.cloud.openfeign.components.DecoratedDecoder; + +import java.lang.reflect.Field; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class DecoderComponentAssert extends FeignComponentAssert { + + @Override + protected Decoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + Class responseHandlerClass = ResponseHandler.class; + Field decoderField = responseHandlerClass.getDeclaredField("decoder"); + decoderField.setAccessible(true); + DecoratedDecoder decoder = (DecoratedDecoder)decoderField.get(responseHandler); + return decoder.delegate(); + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/AEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/AEncoder.java index a9883bad..23abbb89 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/AEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/AEncoder.java @@ -14,7 +14,6 @@ public class AEncoder implements Encoder { @Override public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { - System.out.println("AEncoder is working..."); template.body(object.toString()); } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/BEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/BEncoder.java index 302c04e2..1d3300d1 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/BEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/BEncoder.java @@ -14,7 +14,6 @@ public class BEncoder implements Encoder { @Override public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { - System.out.println("BEncoder is working..."); template.body(object.toString()); } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java index 7989641c..1229021d 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java @@ -2,6 +2,7 @@ import feign.codec.Encoder; import io.microsphere.spring.cloud.openfeign.BaseTest; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -13,6 +14,17 @@ @EnableAutoConfiguration public class EncoderChangedTest extends BaseTest { + + @Override + protected Class beforeTestComponentClass() { + return AEncoder.class; + } + + @Override + protected FeignComponentAssert loadFeignComponentAssert() { + return EncoderComponentAssert.INSTANCE; + } + @Override protected String afterTestComponentConfigKey() { return "spring.cloud.openfeign.client.config.my-client.encoder"; diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java new file mode 100644 index 00000000..41ecfd5a --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java @@ -0,0 +1,35 @@ +package io.microsphere.spring.cloud.openfeign.encoder; + +import feign.MethodHandlerConfiguration; +import feign.ResponseHandler; +import feign.codec.Encoder; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; +import io.microsphere.spring.cloud.openfeign.components.DecoratedEncoder; + +import java.lang.reflect.Field; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class EncoderComponentAssert extends FeignComponentAssert { + + public static final EncoderComponentAssert INSTANCE = new EncoderComponentAssert(); + + private EncoderComponentAssert() { + + } + + @Override + protected Encoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; + Field buildTemplateFromArgs = methodHandlerConfigurationClass.getDeclaredField("buildTemplateFromArgs"); + buildTemplateFromArgs.setAccessible(true); + Object buildTemplateFromArgsValue = buildTemplateFromArgs.get(configuration); + Class buildTemplateFromArgsType = buildTemplateFromArgsValue.getClass(); + Field encoderField = buildTemplateFromArgsType.getDeclaredField("encoder"); + encoderField.setAccessible(true); + DecoratedEncoder encoder = (DecoratedEncoder)encoderField.get(buildTemplateFromArgsValue); + return encoder.delegate(); + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java index 7fb72d91..b9a9d2a3 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java @@ -15,7 +15,6 @@ public class AErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { - log.trace("Error decoding {}", methodKey); return new IllegalArgumentException(""); } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/BErrorEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/BErrorEncoder.java new file mode 100644 index 00000000..4c7bac0f --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/BErrorEncoder.java @@ -0,0 +1,16 @@ +package io.microsphere.spring.cloud.openfeign.errordecoder; + +import feign.Response; +import feign.codec.ErrorDecoder; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class BErrorEncoder implements ErrorDecoder { + + @Override + public Exception decode(String methodKey, Response response) { + return null; + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java index 79a2e1b4..c0140792 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java @@ -2,6 +2,7 @@ import feign.codec.ErrorDecoder; import io.microsphere.spring.cloud.openfeign.BaseTest; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -13,6 +14,16 @@ @EnableAutoConfiguration public class ErrorDecoderChangedTest extends BaseTest { + @Override + protected Class beforeTestComponentClass() { + return AErrorDecoder.class; + } + + @Override + protected FeignComponentAssert loadFeignComponentAssert() { + return new ErrorDecoderComponentAssert(); + } + @Override protected String afterTestComponentConfigKey() { return "spring.cloud.openfeign.client.config.my-client.error-decoder"; @@ -20,6 +31,6 @@ protected String afterTestComponentConfigKey() { @Override protected Class afterTestComponent() { - return AErrorDecoder.class; + return BErrorEncoder.class; } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java new file mode 100644 index 00000000..1352c83d --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java @@ -0,0 +1,25 @@ +package io.microsphere.spring.cloud.openfeign.errordecoder; + +import feign.MethodHandlerConfiguration; +import feign.ResponseHandler; +import feign.codec.ErrorDecoder; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; +import io.microsphere.spring.cloud.openfeign.components.DecoratedErrorDecoder; + +import java.lang.reflect.Field; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class ErrorDecoderComponentAssert extends FeignComponentAssert { + + @Override + protected ErrorDecoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + Class responseHandlerClass = ResponseHandler.class; + Field errorDecoderField = responseHandlerClass.getDeclaredField("errorDecoder"); + errorDecoderField.setAccessible(true); + DecoratedErrorDecoder errorDecoder = (DecoratedErrorDecoder)errorDecoderField.get(responseHandler); + return errorDecoder.delegate(); + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java index 4cde8f05..79e20f11 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java @@ -18,7 +18,6 @@ public class AQueryMapEncoder implements QueryMapEncoder { @Override public Map encode(Object object) { - log.trace("AQueryMapEncoder.encode({})", object); return new HashMap<>(); } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java index 08032694..7d271d47 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java @@ -18,7 +18,6 @@ public class BQueryMapEncoder implements QueryMapEncoder { @Override public Map encode(Object object) { - log.trace("AQueryMapEncoder.encode({})", object); return new HashMap<>(); } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java index 14f9ba8f..1afa7dfd 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java @@ -2,6 +2,7 @@ import feign.QueryMapEncoder; import io.microsphere.spring.cloud.openfeign.BaseTest; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -13,6 +14,16 @@ @EnableAutoConfiguration public class QueryMapEncoderChangedTest extends BaseTest { + @Override + protected Class beforeTestComponentClass() { + return AQueryMapEncoder.class; + } + + @Override + protected FeignComponentAssert loadFeignComponentAssert() { + return new QueryMapEncoderComponentAssert(); + } + @Override protected String afterTestComponentConfigKey() { return "spring.cloud.openfeign.client.config.my-client.query-map-encoder"; diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java new file mode 100644 index 00000000..f3c8d92a --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java @@ -0,0 +1,29 @@ +package io.microsphere.spring.cloud.openfeign.querymapencoder; + +import feign.MethodHandlerConfiguration; +import feign.QueryMapEncoder; +import feign.ResponseHandler; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; +import io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder; + +import java.lang.reflect.Field; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class QueryMapEncoderComponentAssert extends FeignComponentAssert { + + @Override + protected QueryMapEncoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; + Field buildTemplateFromArgs = methodHandlerConfigurationClass.getDeclaredField("buildTemplateFromArgs"); + buildTemplateFromArgs.setAccessible(true); + Object buildTemplateFromArgsValue = buildTemplateFromArgs.get(configuration); + Class buildTemplateFromArgsType = buildTemplateFromArgsValue.getClass().getSuperclass(); + Field encoderField = buildTemplateFromArgsType.getDeclaredField("queryMapEncoder"); + encoderField.setAccessible(true); + DecoratedQueryMapEncoder encoder = (DecoratedQueryMapEncoder)encoderField.get(buildTemplateFromArgsValue); + return encoder.delegate(); + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/ARequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/ARequestInterceptor.java index 84ffa4d6..d7e8fc0b 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/ARequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/ARequestInterceptor.java @@ -11,6 +11,5 @@ public class ARequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { - System.out.println(getClass() + ": A is working..."); } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/BRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/BRequestInterceptor.java index 108f58cf..b06dd608 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/BRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/BRequestInterceptor.java @@ -11,6 +11,5 @@ public class BRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { - System.out.println(getClass() + "B is working..."); } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java index f9651f15..b290206a 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java @@ -2,6 +2,7 @@ import feign.RequestInterceptor; import io.microsphere.spring.cloud.openfeign.BaseTest; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -13,6 +14,16 @@ @EnableAutoConfiguration public class RequestInterceptorChangedTest extends BaseTest { + @Override + protected Class beforeTestComponentClass() { + return ARequestInterceptor.class; + } + + @Override + protected FeignComponentAssert loadFeignComponentAssert() { + return new RequestInterceptorComponentAssert(); + } + @Override protected String afterTestComponentConfigKey() { return "spring.cloud.openfeign.client.config.my-client.request-interceptors[0]"; diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java new file mode 100644 index 00000000..cbb9b840 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java @@ -0,0 +1,42 @@ +package io.microsphere.spring.cloud.openfeign.requestInterceptor; + +import feign.MethodHandlerConfiguration; +import feign.RequestInterceptor; +import feign.ResponseHandler; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; +import io.microsphere.spring.cloud.openfeign.components.CompositedRequestInterceptor; +import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; + +import java.lang.reflect.Field; +import java.util.List; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class RequestInterceptorComponentAssert extends FeignComponentAssert { + + @Override + protected CompositedRequestInterceptor loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; + Field retryField = methodHandlerConfigurationClass.getDeclaredField("requestInterceptors"); + retryField.setAccessible(true); + List retryer = (List) retryField.get(configuration); + for (RequestInterceptor interceptor : retryer) { + if (interceptor instanceof CompositedRequestInterceptor) { + return (CompositedRequestInterceptor) interceptor; + } + } + return null; + } + + @Override + public boolean expect(MethodHandlerConfiguration configuration, ResponseHandler responseHandler, Class expectedClass) throws Exception { + CompositedRequestInterceptor requestInterceptor = loadCurrentComponent(configuration, responseHandler); + for(RequestInterceptor interceptor : requestInterceptor.getRequestInterceptors()) { + if (expectedClass.equals(interceptor.getClass())) + return true; + } + return false; + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/BRetry.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/BRetry.java new file mode 100644 index 00000000..b8ac51c0 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/BRetry.java @@ -0,0 +1,21 @@ +package io.microsphere.spring.cloud.openfeign.retryer; + +import feign.RetryableException; +import feign.Retryer; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class BRetry implements Retryer { + + @Override + public void continueOrPropagate(RetryableException e) { + + } + + @Override + public Retryer clone() { + return this; + } +} diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java index 5139dfc6..1ac2c785 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java @@ -2,6 +2,7 @@ import feign.Retryer; import io.microsphere.spring.cloud.openfeign.BaseTest; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -13,6 +14,16 @@ @EnableAutoConfiguration public class RetryerChangedTest extends BaseTest { + @Override + protected Class beforeTestComponentClass() { + return ARetry.class; + } + + @Override + protected FeignComponentAssert loadFeignComponentAssert() { + return new RetryerComponentAssert(); + } + @Override protected String afterTestComponentConfigKey() { return "spring.cloud.openfeign.client.config.my-client.retryer"; @@ -20,6 +31,6 @@ protected String afterTestComponentConfigKey() { @Override protected Class afterTestComponent() { - return ARetry.class; + return BRetry.class; } } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java new file mode 100644 index 00000000..66dc0479 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java @@ -0,0 +1,25 @@ +package io.microsphere.spring.cloud.openfeign.retryer; + +import feign.MethodHandlerConfiguration; +import feign.ResponseHandler; +import feign.Retryer; +import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; +import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; + +import java.lang.reflect.Field; + +/** + * @author 韩超 + * @since 0.0.1 + */ +public class RetryerComponentAssert extends FeignComponentAssert { + + @Override + protected Retryer loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; + Field retryField = methodHandlerConfigurationClass.getDeclaredField("retryer"); + retryField.setAccessible(true); + DecoratedRetryer retryer = (DecoratedRetryer) retryField.get(configuration); + return retryer.delegate(); + } +} From f78e70370f9871314474910fa9bd9afcf6b50a87 Mon Sep 17 00:00:00 2001 From: Kurok1 Date: Fri, 17 Jan 2025 11:39:45 +0800 Subject: [PATCH 022/310] test support spring-cloud 2022 --- .../cloud/openfeign/FeignComponentAssert.java | 5 ++--- .../ObservableFeignInvocationHandler.java | 20 +++++++++++-------- .../decoder/DecoderComponentAssert.java | 3 +-- .../encoder/EncoderComponentAssert.java | 7 +++---- .../ErrorDecoderComponentAssert.java | 3 +-- .../QueryMapEncoderComponentAssert.java | 7 +++---- .../RequestInterceptorComponentAssert.java | 10 ++++------ .../retryer/RetryerComponentAssert.java | 7 +++---- 8 files changed, 29 insertions(+), 33 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java index 485239ce..57f56d86 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/FeignComponentAssert.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.openfeign; -import feign.MethodHandlerConfiguration; import feign.ResponseHandler; /** @@ -10,9 +9,9 @@ public abstract class FeignComponentAssert { - protected abstract T loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception; + protected abstract T loadCurrentComponent(Object configuration, ResponseHandler responseHandler) throws Exception; - public boolean expect(MethodHandlerConfiguration configuration, ResponseHandler responseHandler, Class expectedClass) throws Exception { + public boolean expect(Object configuration, ResponseHandler responseHandler, Class expectedClass) throws Exception { T component = loadCurrentComponent(configuration, responseHandler); return expectedClass.equals(component.getClass()); } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java index 6fdbae80..b23280bd 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java @@ -1,12 +1,12 @@ package io.microsphere.spring.cloud.openfeign; import feign.InvocationHandlerFactory; -import feign.MethodHandlerConfiguration; import feign.ResponseHandler; import feign.Target; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; @@ -55,18 +55,22 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } InvocationHandlerFactory.MethodHandler methodHandler = dispatch.get(method); - MethodHandlerConfiguration methodHandlerConfiguration = loadMethodHandlerConfiguration(methodHandler); + Object methodHandlerConfiguration = loadMethodHandlerConfiguration(methodHandler); ResponseHandler responseHandler = loadResponseHandler(methodHandler); Assert.isTrue(componentAssert.expect(methodHandlerConfiguration, responseHandler, expectComponentClass), "unexpected component"); - log.trace("component validation is True"); + log.info("component validation is True"); return dispatch.get(method).invoke(args); } - protected MethodHandlerConfiguration loadMethodHandlerConfiguration(InvocationHandlerFactory.MethodHandler methodHandler) throws Exception { - Class configurationType = methodHandler.getClass(); - Field field = configurationType.getDeclaredField("methodHandlerConfiguration"); - field.setAccessible(true); - return (MethodHandlerConfiguration) field.get(methodHandler); + protected Object loadMethodHandlerConfiguration(InvocationHandlerFactory.MethodHandler methodHandler) throws Exception { + if (ClassUtils.isPresent("feign.MethodHandlerConfiguration", ObservableFeignInvocationHandler.class.getClassLoader())) { + Class configurationType = methodHandler.getClass(); + Field field = configurationType.getDeclaredField("methodHandlerConfiguration"); + field.setAccessible(true); + return field.get(methodHandler); + } + return methodHandler; + } protected ResponseHandler loadResponseHandler(InvocationHandlerFactory.MethodHandler methodHandler) throws Exception { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java index 2502f4de..c43b0c4a 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderComponentAssert.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.openfeign.decoder; -import feign.MethodHandlerConfiguration; import feign.ResponseHandler; import feign.codec.Decoder; import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; @@ -15,7 +14,7 @@ public class DecoderComponentAssert extends FeignComponentAssert { @Override - protected Decoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + protected Decoder loadCurrentComponent(Object configuration, ResponseHandler responseHandler) throws Exception { Class responseHandlerClass = ResponseHandler.class; Field decoderField = responseHandlerClass.getDeclaredField("decoder"); decoderField.setAccessible(true); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java index 41ecfd5a..b79c0d35 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderComponentAssert.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.openfeign.encoder; -import feign.MethodHandlerConfiguration; import feign.ResponseHandler; import feign.codec.Encoder; import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; @@ -21,9 +20,9 @@ private EncoderComponentAssert() { } @Override - protected Encoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { - Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; - Field buildTemplateFromArgs = methodHandlerConfigurationClass.getDeclaredField("buildTemplateFromArgs"); + protected Encoder loadCurrentComponent(Object configuration, ResponseHandler responseHandler) throws Exception { + Class configurationClass = configuration.getClass(); + Field buildTemplateFromArgs = configurationClass.getDeclaredField("buildTemplateFromArgs"); buildTemplateFromArgs.setAccessible(true); Object buildTemplateFromArgsValue = buildTemplateFromArgs.get(configuration); Class buildTemplateFromArgsType = buildTemplateFromArgsValue.getClass(); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java index 1352c83d..10892595 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderComponentAssert.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.openfeign.errordecoder; -import feign.MethodHandlerConfiguration; import feign.ResponseHandler; import feign.codec.ErrorDecoder; import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; @@ -15,7 +14,7 @@ public class ErrorDecoderComponentAssert extends FeignComponentAssert { @Override - protected ErrorDecoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { + protected ErrorDecoder loadCurrentComponent(Object configuration, ResponseHandler responseHandler) throws Exception { Class responseHandlerClass = ResponseHandler.class; Field errorDecoderField = responseHandlerClass.getDeclaredField("errorDecoder"); errorDecoderField.setAccessible(true); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java index f3c8d92a..9eeb1802 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderComponentAssert.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.openfeign.querymapencoder; -import feign.MethodHandlerConfiguration; import feign.QueryMapEncoder; import feign.ResponseHandler; import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; @@ -15,9 +14,9 @@ public class QueryMapEncoderComponentAssert extends FeignComponentAssert { @Override - protected QueryMapEncoder loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { - Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; - Field buildTemplateFromArgs = methodHandlerConfigurationClass.getDeclaredField("buildTemplateFromArgs"); + protected QueryMapEncoder loadCurrentComponent(Object configuration, ResponseHandler responseHandler) throws Exception { + Class configurationClass = configuration.getClass(); + Field buildTemplateFromArgs = configurationClass.getDeclaredField("buildTemplateFromArgs"); buildTemplateFromArgs.setAccessible(true); Object buildTemplateFromArgsValue = buildTemplateFromArgs.get(configuration); Class buildTemplateFromArgsType = buildTemplateFromArgsValue.getClass().getSuperclass(); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java index cbb9b840..c3eb60a6 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorComponentAssert.java @@ -1,11 +1,9 @@ package io.microsphere.spring.cloud.openfeign.requestInterceptor; -import feign.MethodHandlerConfiguration; import feign.RequestInterceptor; import feign.ResponseHandler; import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; import io.microsphere.spring.cloud.openfeign.components.CompositedRequestInterceptor; -import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; import java.lang.reflect.Field; import java.util.List; @@ -17,9 +15,9 @@ public class RequestInterceptorComponentAssert extends FeignComponentAssert { @Override - protected CompositedRequestInterceptor loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { - Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; - Field retryField = methodHandlerConfigurationClass.getDeclaredField("requestInterceptors"); + protected CompositedRequestInterceptor loadCurrentComponent(Object configuration, ResponseHandler responseHandler) throws Exception { + Class configurationClass = configuration.getClass(); + Field retryField = configurationClass.getDeclaredField("requestInterceptors"); retryField.setAccessible(true); List retryer = (List) retryField.get(configuration); for (RequestInterceptor interceptor : retryer) { @@ -31,7 +29,7 @@ protected CompositedRequestInterceptor loadCurrentComponent(MethodHandlerConfigu } @Override - public boolean expect(MethodHandlerConfiguration configuration, ResponseHandler responseHandler, Class expectedClass) throws Exception { + public boolean expect(Object configuration, ResponseHandler responseHandler, Class expectedClass) throws Exception { CompositedRequestInterceptor requestInterceptor = loadCurrentComponent(configuration, responseHandler); for(RequestInterceptor interceptor : requestInterceptor.getRequestInterceptors()) { if (expectedClass.equals(interceptor.getClass())) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java index 66dc0479..19dc78c1 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerComponentAssert.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.openfeign.retryer; -import feign.MethodHandlerConfiguration; import feign.ResponseHandler; import feign.Retryer; import io.microsphere.spring.cloud.openfeign.FeignComponentAssert; @@ -15,9 +14,9 @@ public class RetryerComponentAssert extends FeignComponentAssert { @Override - protected Retryer loadCurrentComponent(MethodHandlerConfiguration configuration, ResponseHandler responseHandler) throws Exception { - Class methodHandlerConfigurationClass = MethodHandlerConfiguration.class; - Field retryField = methodHandlerConfigurationClass.getDeclaredField("retryer"); + protected Retryer loadCurrentComponent(Object configuration, ResponseHandler responseHandler) throws Exception { + Class configurationClass = configuration.getClass(); + Field retryField = configurationClass.getDeclaredField("retryer"); retryField.setAccessible(true); DecoratedRetryer retryer = (DecoratedRetryer) retryField.get(configuration); return retryer.delegate(); From e2fab427312e47d05db12d081fc5740d2f848056 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 17 Jan 2025 18:31:47 +0800 Subject: [PATCH 023/310] Reimport --- ...WebMvcServiceRegistryAutoConfigurationTest.java | 1 - .../FeignClientAutoRefreshAutoConfiguration.java | 1 - .../autorefresh/AutoRefreshCapability.java | 14 ++++++++++++-- .../components/CompositedRequestInterceptor.java | 7 ++++++- .../spring/cloud/openfeign/BaseTest.java | 2 -- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java index f0693bf7..6f68ee99 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java @@ -18,7 +18,6 @@ import io.microsphere.spring.webmvc.annotation.EnableWebMvcExtension; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java index 6eacb45c..46f2e3d5 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignBuilderCustomizer; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.context.ConfigurableApplicationContext; diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 9fe6808b..54981d3a 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -1,10 +1,20 @@ package io.microsphere.spring.cloud.openfeign.autorefresh; -import feign.*; +import feign.Capability; +import feign.Contract; +import feign.QueryMapEncoder; +import feign.RequestInterceptor; +import feign.Retryer; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; -import io.microsphere.spring.cloud.openfeign.components.*; +import io.microsphere.spring.cloud.openfeign.components.DecoratedContract; +import io.microsphere.spring.cloud.openfeign.components.DecoratedDecoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedEncoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedErrorDecoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent; +import io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder; +import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; import org.springframework.beans.BeansException; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index d7237a9f..c4d40656 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -7,7 +7,12 @@ import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.util.CollectionUtils; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * @author 韩超 diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java index 6f7a3d78..47baa70b 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java @@ -1,8 +1,6 @@ package io.microsphere.spring.cloud.openfeign; import io.microsphere.spring.cloud.openfeign.autoconfigure.EnableFeignAutoRefresh; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From c52f72142bc2646bea262c948c0988bc1012e5f9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 17 Jan 2025 18:37:48 +0800 Subject: [PATCH 024/310] Update AutoRefreshCapability.java --- .../cloud/openfeign/autorefresh/AutoRefreshCapability.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 54981d3a..dbd23182 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -29,13 +29,13 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAware { private final FeignComponentRegistry componentRegistry; - //private final FeignClientFactory feignClientFactory; + private final NamedContextFactory contextFactory; + private final FeignClientProperties clientProperties; private String contextId; - public AutoRefreshCapability(FeignClientProperties clientProperties, NamedContextFactory contextFactory, FeignComponentRegistry componentRegistry) { this.clientProperties = clientProperties; this.contextFactory = contextFactory; From 1069851a93d7e823d18c5730ad10b57bb81cf73f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 17 Jan 2025 18:46:02 +0800 Subject: [PATCH 025/310] Merge 1.x branch code --- .../constants/DiscoveryClientConstants.java | 8 +- .../event/ServiceInstancesChangedEvent.java | 11 +- ...oServiceRegistrationAutoConfiguration.java | 5 +- .../event/RegistrationDeregisteredEvent.java | 10 +- .../registry/event/RegistrationEvent.java | 49 ++++++- .../RegistrationPreDeregisteredEvent.java | 11 +- .../event/RegistrationPreRegisteredEvent.java | 11 +- .../event/RegistrationRegisteredEvent.java | 11 +- .../DiscoveryClientAutoConfigurationTest.java | 82 +++++++++++ .../UnionDiscoveryClientTest.java | 6 +- .../DiscoveryClientConstantsTest.java | 48 ++++++ .../ServiceInstancesChangedEventTest.java | 67 +++++++++ ...strationEndpointAutoConfigurationTest.java | 45 ++++++ .../ServiceRegistryAutoConfigurationTest.java | 137 ++++++++++++++++++ ...viceRegistrationAutoConfigurationTest.java | 53 +++++++ ...cServiceRegistryAutoConfigurationTest.java | 2 +- .../CommonsPropertyConstantsTest.java | 55 +++++++ 17 files changed, 560 insertions(+), 51 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java index a41950f8..e1fb94c0 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java @@ -32,17 +32,17 @@ public interface DiscoveryClientConstants { /** - * The property prefix of {@link DiscoveryClient} + * The property prefix of {@link DiscoveryClient} : "microsphere.spring.cloud.client.discovery." */ - String DISCOVERY_CLIENT_PROPERTY_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "client.discovery"; + String DISCOVERY_CLIENT_PROPERTY_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "client.discovery."; /** - * The property name of "mode" + * The property name of mode : "mode" */ String MODE_PROPERTY_NAME = "mode"; /** - * The {@link DiscoveryClient} "mode" for {@link UnionDiscoveryClient} + * The {@link DiscoveryClient} "mode" for {@link UnionDiscoveryClient} : "union" */ String UNION_DISCOVERY_CLIENT_MODE = "union"; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java index e13600c2..805706ae 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java @@ -16,13 +16,6 @@ */ package io.microsphere.spring.cloud.client.event; -/** - * TODO Comment - * - * @author Mercy - * @since TODO - */ - import org.springframework.cloud.client.ServiceInstance; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.ApplicationEventMulticaster; @@ -31,9 +24,10 @@ import java.util.List; import static java.util.Collections.unmodifiableList; +import static org.springframework.util.Assert.notEmpty; /** - * An event raised after the {@link ServiceInstance instances} of one service has been + * An event raised when the {@link ServiceInstance instances} of one service has been * changed. * * @author Mercy @@ -58,6 +52,7 @@ public class ServiceInstancesChangedEvent extends ApplicationEvent { public ServiceInstancesChangedEvent(String serviceName, List serviceInstances) { super(serviceName); + notEmpty(serviceInstances, () -> "The arguments 'serviceInstances' must not be empty!"); this.serviceInstances = unmodifiableList(serviceInstances); } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java index 7115ef0a..81b46618 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java @@ -63,9 +63,9 @@ public class SimpleAutoServiceRegistrationAutoConfiguration { /** - * The property name prefix + * The property name prefix : "microsphere.spring.cloud.service-registry.auto-registration.simple." */ - public static final String PROPERTY_NAME_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "simple."; + public static final String PROPERTY_NAME_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "service-registry.auto-registration.simple."; @Bean public Registration registration( @@ -85,7 +85,6 @@ public Registration registration( return registration; } - @Bean @ConditionalOnMissingBean public ServiceRegistry serviceRegistry() { diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java index 294583c2..898b0397 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java @@ -19,6 +19,8 @@ import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.DEREGISTERED; + /** * The after-{@link ServiceRegistry#deregister(Registration) deregister} event. * @@ -33,12 +35,8 @@ public RegistrationDeregisteredEvent(ServiceRegistry registry, Reg } @Override - public boolean isRegistered() { - return false; + public Type getType() { + return DEREGISTERED; } - @Override - public boolean isDeregistered() { - return true; - } } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java index d51d26a6..b8e1b26f 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java @@ -22,6 +22,11 @@ import org.springframework.lang.NonNull; import org.springframework.util.Assert; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.DEREGISTERED; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_DEREGISTERED; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_REGISTERED; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.REGISTERED; + /** * The Spring event for {@link ServiceRegistry} * @@ -80,7 +85,7 @@ public ServiceRegistry getRegistry() { * @return true if pre-registered */ public final boolean isPreRegistered() { - return !isRegistered(); + return getType() == PRE_REGISTERED; } /** @@ -89,7 +94,9 @@ public final boolean isPreRegistered() { * * @return true if registered */ - public abstract boolean isRegistered(); + public final boolean isRegistered() { + return getType() == REGISTERED; + } /** * Current event is raised before the {@link #getRegistration() registration} is @@ -98,7 +105,7 @@ public final boolean isPreRegistered() { * @return true if pre-deregistered */ public final boolean isPreDeregistered() { - return !isDeregistered(); + return getType() == PRE_DEREGISTERED; } /** @@ -107,6 +114,40 @@ public final boolean isPreDeregistered() { * * @return true if deregistered */ - public abstract boolean isDeregistered(); + public final boolean isDeregistered() { + return getType() == DEREGISTERED; + } + + /** + * Get the {@link Type} of the {@link RegistrationEvent} + * + * @return non-null + */ + public abstract Type getType(); + /** + * The {@link Type} of the {@link RegistrationEvent} + */ + public static enum Type { + + /** + * The {@link RegistrationPreRegisteredEvent} + */ + PRE_REGISTERED, + + /** + * The {@link RegistrationRegisteredEvent} + */ + REGISTERED, + + /** + * The {@link RegistrationPreDeregisteredEvent} + */ + PRE_DEREGISTERED, + + /** + * The {@link RegistrationDeregisteredEvent} + */ + DEREGISTERED + } } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java index 6e194752..3ef61b67 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java @@ -19,6 +19,8 @@ import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_DEREGISTERED; + /** * The before-{@link ServiceRegistry#deregister(Registration) deregister} event. * @@ -33,12 +35,7 @@ public RegistrationPreDeregisteredEvent(ServiceRegistry registry, } @Override - public boolean isRegistered() { - return false; - } - - @Override - public boolean isDeregistered() { - return false; + public Type getType() { + return PRE_DEREGISTERED; } } \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java index 2e0d192d..ccceec17 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java @@ -19,6 +19,8 @@ import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_REGISTERED; + /** * The before-{@link ServiceRegistry#register(Registration) register} event. * @@ -33,12 +35,7 @@ public RegistrationPreRegisteredEvent(ServiceRegistry registry, Re } @Override - public boolean isRegistered() { - return false; - } - - @Override - public boolean isDeregistered() { - return false; + public Type getType() { + return PRE_REGISTERED; } } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java index cc20ed1d..06306ac8 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java @@ -19,6 +19,8 @@ import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.REGISTERED; + /** * The after-{@link ServiceRegistry#register(Registration) register} event. * @@ -33,12 +35,7 @@ public RegistrationRegisteredEvent(ServiceRegistry registry, Regis } @Override - public boolean isRegistered() { - return true; - } - - @Override - public boolean isDeregistered() { - return false; + public Type getType() { + return REGISTERED; } } \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java new file mode 100644 index 00000000..a13bb348 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.microsphere.spring.cloud.client.discovery.autoconfigure; + +import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; +import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClientTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; +import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClient; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration; +import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link DiscoveryClientAutoConfiguration} Test + * + * @author Mercy + * @see DiscoveryClientAutoConfiguration + * @since 1.0.0 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = { + UtilAutoConfiguration.class, + SimpleDiscoveryClientAutoConfiguration.class, + CompositeDiscoveryClientAutoConfiguration.class, + DiscoveryClientAutoConfiguration.class, + UnionDiscoveryClientTest.class +}) +@TestPropertySource( + properties = { + "microsphere.spring.cloud.client.discovery.mode=union", + "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", + "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", + "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", + "spring.cloud.discovery.client.simple.instances.test[0].port=8080", + "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1" + } +) +public class DiscoveryClientAutoConfigurationTest { + + @Autowired + private DiscoveryClient discoveryClient; + + @Test + public void test() { + assertEquals(CompositeDiscoveryClient.class, discoveryClient.getClass()); + CompositeDiscoveryClient compositeDiscoveryClient = CompositeDiscoveryClient.class.cast(discoveryClient); + List discoveryClients = compositeDiscoveryClient.getDiscoveryClients(); + assertEquals(2, discoveryClients.size()); + assertEquals(UnionDiscoveryClient.class, discoveryClients.get(0).getClass()); + assertEquals(SimpleDiscoveryClient.class, discoveryClients.get(1).getClass()); + List services = compositeDiscoveryClient.getServices(); + assertEquals(Arrays.asList("test"), services); + assertEquals(services, discoveryClients.get(0).getServices()); + assertEquals(services, discoveryClients.get(1).getServices()); + } +} diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java index 882ae70e..513a4a34 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java @@ -14,9 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.spring.cloud.client.discovery.autoconfigure; +package io.microsphere.spring.cloud.client.discovery; -import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -46,12 +45,11 @@ UtilAutoConfiguration.class, SimpleDiscoveryClientAutoConfiguration.class, CompositeDiscoveryClientAutoConfiguration.class, - DiscoveryClientAutoConfiguration.class, + UnionDiscoveryClient.class, UnionDiscoveryClientTest.class }) @TestPropertySource( properties = { - "microsphere.spring.cloud.client.discovery.mode=union", "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java new file mode 100644 index 00000000..65851f68 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.microsphere.spring.cloud.client.discovery.constants; + +import org.junit.jupiter.api.Test; + +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_PROPERTY_PREFIX; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.MODE_PROPERTY_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.UNION_DISCOVERY_CLIENT_MODE; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link DiscoveryClientConstants} Test + * + * @author Mercy + * @see DiscoveryClientConstants + * @since 1.0.0 + */ +public class DiscoveryClientConstantsTest { + + @Test + public void testConstants() { + assertEquals("microsphere.spring.cloud.client.discovery.", DISCOVERY_CLIENT_PROPERTY_PREFIX); + assertEquals("mode", MODE_PROPERTY_NAME); + assertEquals("union", UNION_DISCOVERY_CLIENT_MODE); + + assertEquals("org.springframework.cloud.client.discovery.DiscoveryClient", DISCOVERY_CLIENT_CLASS_NAME); + assertEquals("org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient", COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME); + assertEquals("org.springframework.cloud.client.CommonsClientAutoConfiguration", COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); + } +} diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java new file mode 100644 index 00000000..000595e6 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java @@ -0,0 +1,67 @@ +package io.microsphere.spring.cloud.client.event; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; + +import java.net.URI; +import java.util.Arrays; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ServiceInstancesChangedEvent} Test + * + * @author Mercy + * @see ServiceInstancesChangedEvent + * @since 1.0.0 + */ +public class ServiceInstancesChangedEventTest { + + private String serviceName = "testService"; + + private ServiceInstancesChangedEvent event; + + private ServiceInstance instance; + + @BeforeEach + public void init() { + this.instance = createInstance(serviceName); + this.event = new ServiceInstancesChangedEvent(serviceName, Arrays.asList(instance)); + } + + private ServiceInstance createInstance(String serviceName) { + DefaultServiceInstance instance = new DefaultServiceInstance(); + instance.setServiceId(serviceName); + instance.setServiceId(UUID.randomUUID().toString()); + instance.setHost("127.0.0.1"); + instance.setPort(8080); + instance.setUri(URI.create("http://127.0.0.1:8080/info")); + return instance; + } + + @Test + public void testGetServiceName() { + assertEquals(this.serviceName, this.event.getServiceName()); + assertEquals(this.serviceName, this.event.getSource()); + } + + @Test + public void testGetServiceInstances() { + assertEquals(Arrays.asList(this.instance), this.event.getServiceInstances()); + assertEquals(this.instance, this.event.getServiceInstances().get(0)); + assertSame(this.instance, this.event.getServiceInstances().get(0)); + } + + @Test + public void testProcessed() { + assertFalse(this.event.isProcessed()); + this.event.processed(); + assertTrue(this.event.isProcessed()); + } +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java new file mode 100644 index 00000000..528396dd --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java @@ -0,0 +1,45 @@ +package io.microsphere.spring.cloud.client.service.registry.actuate.autoconfigure; + +import io.microsphere.spring.cloud.client.service.registry.endpoint.ServiceDeregistrationEndpoint; +import io.microsphere.spring.cloud.client.service.registry.endpoint.ServiceRegistrationEndpoint; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * {@link ServiceRegistrationEndpointAutoConfiguration} Test + * + * @author Mercy + * @see ServiceRegistrationEndpointAutoConfiguration + * @since 1.0.0 + */ +@SpringBootTest( + classes = { + ServiceRegistrationEndpointAutoConfigurationTest.class + }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", + "management.endpoint.serviceRegistration.enabled=true", + "management.endpoint.serviceDeregistration.enabled=true", + } +) +@EnableAutoConfiguration +public class ServiceRegistrationEndpointAutoConfigurationTest { + + @Autowired + private ObjectProvider serviceRegistrationEndpoint; + + @Autowired + private ObjectProvider serviceDeregistrationEndpoint; + + @Test + public void testEndpoints() { + assertNotNull(serviceRegistrationEndpoint); + } + +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java new file mode 100644 index 00000000..4ba1fb5e --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java @@ -0,0 +1,137 @@ +package io.microsphere.spring.cloud.client.service.registry.autoconfigure; + +import io.microsphere.spring.cloud.client.service.registry.DefaultRegistration; +import io.microsphere.spring.cloud.client.service.registry.InMemoryServiceRegistry; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationDeregisteredEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationPreDeregisteredEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationPreRegisteredEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationRegisteredEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.DEREGISTERED; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_DEREGISTERED; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_REGISTERED; +import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.REGISTERED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.aop.support.AopUtils.getTargetClass; + +/** + * {@link ServiceRegistryAutoConfiguration} Test + * + * @author Mercy + * @see ServiceRegistryAutoConfiguration + * @since 1.0.0 + */ +@SpringBootTest( + classes = { + InMemoryServiceRegistry.class, + ServiceRegistryAutoConfiguration.class, + ServiceRegistryAutoConfigurationTest.class + } +) +@EnableAspectJAutoProxy +public class ServiceRegistryAutoConfigurationTest { + + @Autowired + private ConfigurableApplicationContext context; + + @Autowired + private ServiceRegistry serviceRegistry; + + private Registration registration; + + private int count; + + @BeforeEach + public void init() { + DefaultRegistration registration = new DefaultRegistration(); + registration.setServiceId("test-service"); + registration.setInstanceId("127.0.0.1:8080"); + this.registration = registration; + } + + @Test + public void testEventPublishingRegistrationAspect() { + + context.addApplicationListener(this::onApplicationEvent); + + serviceRegistry.register(registration); + + assertEquals(2, count); + + serviceRegistry.deregister(registration); + + assertEquals(4, count); + } + + private void onApplicationEvent(ApplicationEvent event) { + if (event instanceof RegistrationPreRegisteredEvent) { + onRegistrationPreRegisteredEvent((RegistrationPreRegisteredEvent) event); + } else if (event instanceof RegistrationRegisteredEvent) { + onRegistrationRegisteredEvent((RegistrationRegisteredEvent) event); + } else if (event instanceof RegistrationPreDeregisteredEvent) { + onRegistrationPreDeregisteredEvent((RegistrationPreDeregisteredEvent) event); + } else if (event instanceof RegistrationDeregisteredEvent) { + onRegistrationDeregisteredEvent((RegistrationDeregisteredEvent) event); + } + } + + private void onRegistrationPreRegisteredEvent(RegistrationPreRegisteredEvent event) { + assertRegistrationEvent(event); + assertTrue(event.isPreRegistered()); + assertFalse(event.isRegistered()); + assertFalse(event.isPreDeregistered()); + assertFalse(event.isDeregistered()); + assertEquals(PRE_REGISTERED, event.getType()); + } + + private void onRegistrationRegisteredEvent(RegistrationRegisteredEvent event) { + assertRegistrationEvent(event); + assertFalse(event.isPreRegistered()); + assertTrue(event.isRegistered()); + assertFalse(event.isPreDeregistered()); + assertFalse(event.isDeregistered()); + assertEquals(REGISTERED, event.getType()); + } + + private void onRegistrationPreDeregisteredEvent(RegistrationPreDeregisteredEvent event) { + assertRegistrationEvent(event); + assertFalse(event.isPreRegistered()); + assertFalse(event.isRegistered()); + assertTrue(event.isPreDeregistered()); + assertFalse(event.isDeregistered()); + assertEquals(PRE_DEREGISTERED, event.getType()); + } + + private void onRegistrationDeregisteredEvent(RegistrationDeregisteredEvent event) { + assertRegistrationEvent(event); + assertFalse(event.isPreRegistered()); + assertFalse(event.isRegistered()); + assertFalse(event.isPreDeregistered()); + assertTrue(event.isDeregistered()); + assertEquals(DEREGISTERED, event.getType()); + } + + private void assertRegistrationEvent(RegistrationEvent event) { + Registration registration = event.getRegistration(); + assertEquals(this.registration, registration); + assertSame(this.registration, registration); + assertSame(getTargetClass(this.serviceRegistry), getTargetClass(event.getRegistry())); + assertNotNull(event.getSource()); + assertNotNull(event.getType()); + count++; + } +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java new file mode 100644 index 00000000..76f60709 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java @@ -0,0 +1,53 @@ +package io.microsphere.spring.cloud.client.service.registry.autoconfigure; + +import io.microsphere.spring.cloud.client.service.registry.SimpleAutoServiceRegistration; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * {@link SimpleAutoServiceRegistrationAutoConfiguration} Test + * + * @author Mercy + * @see SimpleAutoServiceRegistrationAutoConfiguration + * @since 1.0.0 + */ +@SpringBootTest( + classes = { + SimpleAutoServiceRegistrationAutoConfigurationTest.class + }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", + "spring.application.name=test-service" + } +) +@EnableAutoConfiguration +public class SimpleAutoServiceRegistrationAutoConfigurationTest { + + @Autowired + private Registration registration; + + @Autowired + private ServiceRegistry serviceRegistry; + + @Autowired + private SimpleAutoServiceRegistration simpleAutoServiceRegistration; + + @Test + public void test() { + assertEquals("test-service", registration.getServiceId()); + assertNotNull(registration.getHost()); + assertNotNull(registration.getPort()); + assertNotNull(registration.getUri()); + assertNotNull(registration.getInstanceId()); + assertNotNull(registration.getMetadata()); + } + +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java index 6f68ee99..5f24ed74 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java @@ -37,7 +37,7 @@ @SpringBootTest( classes = {WebMvcServiceRegistryAutoConfigurationTest.class}, properties = { - "microsphere.spring.cloud.simple.enabled=true", + "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", "spring.cloud.service-registry.auto-registration.enabled=true", "spring.cloud.kubernetes.enabled=false", "kubernetes.informer.enabled=false", diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java new file mode 100644 index 00000000..3986340e --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.microsphere.spring.cloud.commons.constants; + +import org.junit.jupiter.api.Test; + +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.COMPOSITE_REGISTRATION_ENABLED_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.FEATURES_ENABLED_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_WEB_MVC_PROPERTY_NAME_PREFIX; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MULTIPLE_REGISTRATION_DEFAULT_REGISTRATION_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MULTIPLE_REGISTRATION_DEFAULT_REGISTRY_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.SERVICE_REGISTRY_PROPERTY_PREFIX; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.SPRING_CLOUD_PROPERTY_PREFIX; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link CommonsPropertyConstants} Test + * + * @author Mercy + * @see CommonsPropertyConstants + * @since 1.0.0 + */ +public class CommonsPropertyConstantsTest { + + @Test + public void testConstants() { + assertEquals("spring.cloud.", SPRING_CLOUD_PROPERTY_PREFIX); + assertEquals("spring.cloud.service-registry.", SERVICE_REGISTRY_PROPERTY_PREFIX); + assertEquals("spring.cloud.service-registry.auto-registration.enabled", SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME); + assertEquals("spring.cloud.features.enabled", FEATURES_ENABLED_PROPERTY_NAME); + assertEquals("microsphere.spring.cloud.", MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX); + assertEquals("microsphere.spring.cloud.web.mvc.", MICROSPHERE_SPRING_CLOUD_WEB_MVC_PROPERTY_NAME_PREFIX); + assertEquals("microsphere.spring.cloud.multiple-registration.enabled", MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME); + assertEquals("microsphere.spring.cloud.default-registration.type", MULTIPLE_REGISTRATION_DEFAULT_REGISTRATION_PROPERTY_NAME); + assertEquals("microsphere.spring.cloud.default-service-registry.type", MULTIPLE_REGISTRATION_DEFAULT_REGISTRY_PROPERTY_NAME); + assertEquals("microsphere.spring.cloud.composite-registration.enabled", COMPOSITE_REGISTRATION_ENABLED_PROPERTY_NAME); + } +} From 2b071f545e7c5fa91351daa97ad68d497bc7d707 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 17 Jan 2025 18:59:01 +0800 Subject: [PATCH 026/310] Remove duplicated dependency 'org.springframework.cloud:spring-cloud-starter-loadbalancer' --- microsphere-spring-cloud-openfeign/pom.xml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/pom.xml b/microsphere-spring-cloud-openfeign/pom.xml index be05c2cc..cf5f9dd1 100644 --- a/microsphere-spring-cloud-openfeign/pom.xml +++ b/microsphere-spring-cloud-openfeign/pom.xml @@ -55,13 +55,7 @@ spring-boot-starter-test test - - - org.springframework.cloud - spring-cloud-starter-loadbalancer - true - - + \ No newline at end of file From 7e3f25f8228eb227b6f7acd92228f04969d7549b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 4 Feb 2025 10:23:00 +0800 Subject: [PATCH 027/310] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 21106849..814e890a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.microsphere-projects microsphere-build - 0.1.0 + 0.1.1 4.0.0 From 35fdecb9d158c5f9bad2ee51d0732ad4e8f978f5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 4 Feb 2025 11:13:30 +0800 Subject: [PATCH 028/310] Update maven-publish.yml --- .github/workflows/maven-publish.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 41170da0..9c4da401 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -37,7 +37,14 @@ jobs: cache: maven - name: Publish package - run: mvn --batch-mode --update-snapshots -Drevision=${{ inputs.revision }} -Dgpg.skip=true -Prelease,ci clean deploy + run: mvn + --batch-mode + --update-snapshots + --file pom.xml + -Drevision=${{ inputs.revision }} + -Dgpg.skip=true + deploy + --activate-profiles release,ci env: MAVEN_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} From 64608772ac5f717b7461ca02701153318996798a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 19:44:48 +0800 Subject: [PATCH 029/310] Update parent pom version to 0.2.1 #58 Bump the microsphere-build parent POM version from 0.1.1 to 0.2.1 to use the latest build configuration and dependencies. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 814e890a..a6038238 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.microsphere-projects microsphere-build - 0.1.1 + 0.2.1 4.0.0 From f15b406d2d235cb9a15612f64737afdca41197a4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 19:49:58 +0800 Subject: [PATCH 030/310] Add Java 25 to CI build matrix #59 Updated the GitHub Actions workflow to include Java 25 in the build matrix, ensuring compatibility and testing with the latest Java version. --- .github/workflows/maven-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 024a6783..8d7fb035 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '17' , '21' ] + java: [ '17' , '21' , '25' ] maven-profile-spring-cloud: [ 'spring-cloud-2022' , 'spring-cloud-2023' , 'spring-cloud-2024' ] steps: - name: Checkout Source From 5c3337334bba9af532e55335254eae4612b9085c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 21:09:34 +0800 Subject: [PATCH 031/310] Fix whitespace in maven-publish workflow Removed extra spaces in conditional and environment variable assignments to ensure consistent formatting in the GitHub Actions workflow. --- .github/workflows/maven-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 9c4da401..29da6fcd 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -21,7 +21,7 @@ on: jobs: build: runs-on: ubuntu-latest - if: ${{ inputs.revision }} + if: ${{ inputs.revision }} steps: - name: Checkout Source uses: actions/checkout@v4 @@ -48,6 +48,6 @@ jobs: env: MAVEN_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} - SIGN_KEY_ID: ${{ secrets.OSS_SIGNING_KEY_ID_LONG }} + SIGN_KEY_ID: ${{ secrets.OSS_SIGNING_KEY_ID_LONG }} SIGN_KEY: ${{ secrets.OSS_SIGNING_KEY }} SIGN_KEY_PASS: ${{ secrets.OSS_SIGNING_PASSWORD }} From 8fc63bff7b6f219c836c2b125cf658e785b8f1a3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 22:22:30 +0800 Subject: [PATCH 032/310] Update Spring Boot and Spring Cloud versions in profiles Bumped Spring Boot and Spring Cloud versions for the 'spring-cloud-2023' and 'spring-cloud-2025' profiles. Added a new 'spring-cloud-2024' profile with corresponding version properties to improve compatibility and support for newer Spring Cloud releases. --- microsphere-spring-cloud-parent/pom.xml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 1dbea262..12917352 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -89,20 +89,29 @@ spring-cloud-2023 - 3.2.12 - 2023.0.5 + 3.3.13 + 2023.0.6 2023.0.3.2 spring-cloud-2024 + + 3.4.7 + 2024.0.2 + 2023.0.3.2 + + + + + spring-cloud-2025 true - 3.4.0 - 2024.0.0 + 3.5.0 + 2025.0.0 2023.0.3.2 From 16c5e199ee6a46e89b47fb4e33f86c00ad8e9bd5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 22:23:28 +0800 Subject: [PATCH 033/310] Add spring-cloud-2025 to Maven build matrix Updated the GitHub Actions workflow to include the 'spring-cloud-2025' Maven profile in the build matrix, enabling CI for the new Spring Cloud version. --- .github/workflows/maven-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 8d7fb035..f9c4cfcf 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: java: [ '17' , '21' , '25' ] - maven-profile-spring-cloud: [ 'spring-cloud-2022' , 'spring-cloud-2023' , 'spring-cloud-2024' ] + maven-profile-spring-cloud: [ 'spring-cloud-2022' , 'spring-cloud-2023' , 'spring-cloud-2024' , 'spring-cloud-2025' ] steps: - name: Checkout Source uses: actions/checkout@v4 From 89c4d52bace667848aac0ac0c88520de60338956 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 22:27:14 +0800 Subject: [PATCH 034/310] Bump microsphere-spring-boot version to 0.2.2 #56 Updated the microsphere-spring-boot.version property in the parent POM from 0.2.1 to 0.2.2 to use the latest release. --- microsphere-spring-cloud-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 12917352..8a22d821 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -20,7 +20,7 @@ - 0.2.1 + 0.2.2 1.20.4 From d7e2c766c5a8f7afb6169255a996f0e2e51c14f5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 22:51:16 +0800 Subject: [PATCH 035/310] Add CODE_OF_CONDUCT.md #62 Introduces a Contributor Code of Conduct to foster an open and welcoming community, outlining expected behavior and reporting procedures. Adapted from the Contributor Covenant v1.3.0. --- CODE_OF_CONDUCT.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..d57aa809 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,48 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of +fostering an open and welcoming community, we pledge to respect all people who +contribute through reporting issues, posting feature requests, updating +documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission +* Other unethical or unprofessional conduct + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to +fairly and consistently applying these principles to every aspect of managing +this project. Project maintainers who do not follow or enforce the Code of +Conduct may be permanently removed from the project team. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting a project maintainer at [mercyblitz@gmail.com](mailto:mercyblitz@gmail.com). All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. Maintainers are +obligated to maintain confidentiality with regard to the reporter of an +incident. + + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.3.0, available at https://www.contributor-covenant.org/version/1/3/0/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org \ No newline at end of file From 1c04561a226a9cb84d9c1ad092723132147c9814 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 13 Oct 2025 22:57:58 +0800 Subject: [PATCH 036/310] Update README with badges and detailed documentation Added DeepWiki and ZRead badges, expanded project overview, purpose, and module descriptions. Included detailed getting started instructions, build steps, contribution guidelines, issue reporting, and links to documentation and JavaDocs to improve clarity and onboarding for new users. --- README.md | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e350487..e9b6dc44 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Microsphere Spring Cloud -Microsphere Projects for Spring Cloud +> Microsphere Projects for Spring Cloud +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/microsphere-projects/microsphere-spring-cloud) +[![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/microsphere-projects/microsphere-spring-cloud) [![Maven Build](https://github.com/microsphere-projects/microsphere-spring-cloud/actions/workflows/maven-build.yml/badge.svg)](https://github.com/microsphere-projects/microsphere-spring-cloud/actions/workflows/maven-build.yml) [![Codecov](https://codecov.io/gh/microsphere-projects/microsphere-spring-cloud/branch/dev/graph/badge.svg)](https://app.codecov.io/gh/microsphere-projects/microsphere-spring-cloud) ![Maven](https://img.shields.io/maven-central/v/io.github.microsphere-projects/microsphere-spring-cloud.svg) @@ -9,3 +11,134 @@ Microsphere Projects for Spring Cloud [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/microsphere-projects/microsphere-spring-cloud.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-cloud "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/microsphere-projects/microsphere-spring-cloud.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-cloud "Percentage of issues still open") +Microsphere Spring Cloud is an extension library for Spring Cloud that enhances and optimizes its capabilities, +particularly focused on providing dynamic runtime configuration changes without application restarts. It's designed to +solve common pain points when working with distributed systems in Spring Cloud. + +## Purpose and Scope + +Microsphere Spring Cloud is a comprehensive extension framework that enhances Spring Cloud applications with advanced +service registration capabilities, dynamic OpenFeign client configuration, and fault tolerance features. This project +provides production-ready enhancements to the core Spring Cloud ecosystem, focusing on operational reliability and +dynamic configuration management. + +The framework supports multiple Spring Cloud versions (2022.x, 2023.x, 2024.x and 2025.x) and integrates seamlessly with +various +service discovery systems including Nacos, Eureka, Consul, and Zookeeper. For detailed information about specific +subsystems, see Project Structure, Service Registration System, OpenFeign Auto-Refresh System, and Fault Tolerance. + +## Modules + +| **Module** | **Purpose** | +|-------------------------------------------|-------------------------------------------------------------------------------------| +| **microsphere-spring-cloud-parent** | Defines the parent POM with dependency management and Spring Cloud version profiles | +| **microsphere-spring-cloud-dependencies** | Centralizes dependency management for all project modules | +| **microsphere-spring-cloud-commons** | Common utilities for service discovery, registry, and fault tolerance | +| **microsphere-spring-cloud-openfeign** | Extensions for Spring Cloud OpenFeign with auto-refresh capabilities | + +## Getting Started + +The easiest way to get started is by adding the Microsphere Spring Cloud BOM (Bill of Materials) to your project's +pom.xml: + +```xml + + + ... + + + io.github.microsphere-projects + microsphere-spring-cloud-dependencies + ${microsphere-spring-cloud.version} + pom + import + + ... + + +``` + +`${microsphere-spring-boot.version}` has two branches: + +| **Branches** | **Purpose** | **Latest Version** | +|--------------|--------------------------------------------------|--------------------| +| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.1 | +| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.1 | + +Then add the specific modules you need: + +```xml + + + + io.github.microsphere-projects + microsphere-spring-cloud-commons + + + + + io.github.microsphere-projects + microsphere-spring-cloud-openfeign + + +``` + +## Building from Source + +You don't need to build from source unless you want to try out the latest code or contribute to the project. + +To build the project, follow these steps: + +1. Clone the repository: + +```bash +git clone https://github.com/microsphere-projects/microsphere-spring-cloud.git +``` + +2. Build the source: + +- Linux/MacOS: + +```bash +./mvnw package +``` + +- Windows: + +```powershell +mvnw.cmd package +``` + +## Contributing + +We welcome your contributions! Please read [Code of Conduct](./CODE_OF_CONDUCT.md) before submitting a pull request. + +## Reporting Issues + +* Before you log a bug, please search + the [issues](https://github.com/microsphere-projects/microsphere-spring-cloud/issues) + to see if someone has already reported the problem. +* If the issue doesn't already + exist, [create a new issue](https://github.com/microsphere-projects/microsphere-spring-cloud/issues/new). +* Please provide as much information as possible with the issue report. + +## Documentation + +### User Guide + +[DeepWiki Host](https://deepwiki.com/microsphere-projects/microsphere-spring-cloud) + +[ZRead Host](https://zread.ai/microsphere-projects/microsphere-spring-cloud) + +### Wiki + +[Github Host](https://github.com/microsphere-projects/microsphere-spring-cloud/wiki) + +### JavaDoc + +- [microsphere-spring-cloud-commons](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-spring-cloud-commons) +- [microsphere-spring-cloud-openfeign](https://javadoc.io/doc/io.github.microsphere-projects/microsphere-spring-cloud-openfeign) + +## License + +The Microsphere Spring is released under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). From 061144c4d31a69debac4648a57e1cd11fb362bdb Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:43:06 +0800 Subject: [PATCH 037/310] Refactor annotation imports in ConditionalOnFeaturesEnabled Replaces explicit annotation class references with static imports for RetentionPolicy and ElementType in ConditionalOnFeaturesEnabled. This improves code readability and consistency. --- .../client/condition/ConditionalOnFeaturesEnabled.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java index 2509dd8a..5bf03040 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java @@ -30,6 +30,9 @@ import java.lang.annotation.Target; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.FEATURES_ENABLED_PROPERTY_NAME; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * The conditional annotation meta-annotates {@link ConditionalOnProperty @ConditionalOnProperty} for @@ -43,8 +46,8 @@ * @see ConditionalOnProperty * @since 1.0.0 */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RUNTIME) +@Target({TYPE, METHOD}) @Documented @ConditionalOnProperty(name = FEATURES_ENABLED_PROPERTY_NAME) public @interface ConditionalOnFeaturesEnabled { From d1df86f432b4eae075c433c19781e62afadba4a9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:43:16 +0800 Subject: [PATCH 038/310] Use EMPTY_STRING_ARRAY in toArray for url patterns Replaces the use of new String[0] with EMPTY_STRING_ARRAY in the toArray call for better clarity and potential performance improvement. --- .../autoconfigure/WebMvcServiceRegistryAutoConfiguration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index df17c7ad..79acc343 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -44,6 +44,7 @@ import java.util.Set; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; +import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; /** @@ -157,7 +158,7 @@ private boolean matchFilter(FilterRegistrationBean filterRegistrationBean, Strin } private boolean matchUrlPatterns(Collection urlPatterns, String[] patterns) { - String[] urlPatternsArray = urlPatterns.isEmpty() ? DEFAULT_URL_MAPPINGS : urlPatterns.toArray(new String[0]); + String[] urlPatternsArray = urlPatterns.isEmpty() ? DEFAULT_URL_MAPPINGS : urlPatterns.toArray(EMPTY_STRING_ARRAY); return Arrays.equals(urlPatternsArray, patterns); } From e9e1b49778157a15b5f76b33e0a34819e125f7b1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:43:28 +0800 Subject: [PATCH 039/310] Enable auto service registration by default Updated @ConditionalOnAutoServiceRegistrationEnabled to set matchIfMissing=true, so auto service registration is enabled by default unless explicitly disabled. Also refactored annotation imports for clarity. --- .../ConditionalOnAutoServiceRegistrationEnabled.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java index 9da6d954..6112d717 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java @@ -27,6 +27,9 @@ import java.lang.annotation.Target; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * The conditional annotation meta-annotates {@link ConditionalOnProperty @ConditionalOnProperty} for @@ -37,10 +40,10 @@ * @see ConditionalOnProperty * @since 1.0.0 */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RUNTIME) +@Target({TYPE, METHOD}) @Documented -@ConditionalOnProperty(name = SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME) +@ConditionalOnProperty(name = SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME, matchIfMissing = true) public @interface ConditionalOnAutoServiceRegistrationEnabled { /** From 507a6c97c047207c24163e89ce10ea2994a541d4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:43:39 +0800 Subject: [PATCH 040/310] Refactor annotation imports in ConditionalOnMultipleRegistrationEnabled Replaces explicit annotation class references with static imports for RetentionPolicy and ElementType. Updates @Retention and @Target annotations to use statically imported constants for improved readability. --- .../ConditionalOnMultipleRegistrationEnabled.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java index 9e854728..f22f4998 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java @@ -10,6 +10,9 @@ import java.lang.annotation.Target; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * {@link Conditional @Conditional} that checks whether the multiple service registry enabled @@ -18,8 +21,8 @@ * @author Mercy * @since 1.0 */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RUNTIME) +@Target({TYPE, METHOD}) @Documented @ConditionalOnProperty(name = MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME) public @interface ConditionalOnMultipleRegistrationEnabled { From 77e0e1d246c37ca3a98b4231403ee8710cc6354e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:43:47 +0800 Subject: [PATCH 041/310] Update Javadoc references to fully qualified class name Replaces simple class references with fully qualified names in Javadoc for better clarity and navigation. --- .../client/condition/ConditionalOnFeaturesEnabledTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java index eab52d40..a3f50dfd 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java @@ -17,10 +17,10 @@ package io.microsphere.spring.cloud.client.condition; /** - * {@link ConditionalOnFeaturesEnabled} Test + * {@link io.microsphere.spring.cloud.client.condition.ConditionalOnFeaturesEnabled} Test * * @author Mercy - * @see ConditionalOnFeaturesEnabled + * @see io.microsphere.spring.cloud.client.condition.ConditionalOnFeaturesEnabled * @since 1.0.0 */ From dae259b9d442d58e1886bfbcefa7a9d79ac44d6a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:43:54 +0800 Subject: [PATCH 042/310] Rename test setup method to setUp Renamed the @BeforeEach method from 'init' to 'setUp' in ServiceInstancesChangedEventTest to follow common JUnit naming conventions. --- .../cloud/client/event/ServiceInstancesChangedEventTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java index 000595e6..74d33a2e 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java @@ -30,7 +30,7 @@ public class ServiceInstancesChangedEventTest { private ServiceInstance instance; @BeforeEach - public void init() { + void setUp() { this.instance = createInstance(serviceName); this.event = new ServiceInstancesChangedEvent(serviceName, Arrays.asList(instance)); } From 02b4e54af3ede449138715ae2187d328c9029e4a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:44:00 +0800 Subject: [PATCH 043/310] Rename test setup method to setUp Renamed the @BeforeEach method from 'init' to 'setUp' in ServiceRegistryAutoConfigurationTest for consistency with common test naming conventions. --- .../autoconfigure/ServiceRegistryAutoConfigurationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java index 4ba1fb5e..340e268e 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java @@ -56,7 +56,7 @@ public class ServiceRegistryAutoConfigurationTest { private int count; @BeforeEach - public void init() { + void setUp() { DefaultRegistration registration = new DefaultRegistration(); registration.setServiceId("test-service"); registration.setInstanceId("127.0.0.1:8080"); From c2373d9e59723f2feb723446ea77f627c032675a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:44:04 +0800 Subject: [PATCH 044/310] Update TomcatFaultToleranceAutoConfigurationTest.java --- .../TomcatFaultToleranceAutoConfigurationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java index 945f5833..3dcbf8de 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java @@ -55,7 +55,7 @@ public class TomcatFaultToleranceAutoConfigurationTest { private AbstractHttp11Protocol protocol; @BeforeEach - public void before() { + void before() { TomcatWebServer tomcatWebServer = (TomcatWebServer) context.getWebServer(); this.tomcat = tomcatWebServer.getTomcat(); this.connector = tomcat.getConnector(); From d02a6f590863d74e50bb82dd53c63dea2e1bf035 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:44:12 +0800 Subject: [PATCH 045/310] Refactor annotation imports in EnableFeignAutoRefresh Replaces direct usage of ElementType.TYPE and RetentionPolicy.RUNTIME with static imports for improved readability in the EnableFeignAutoRefresh annotation. --- .../openfeign/autoconfigure/EnableFeignAutoRefresh.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java index da5ab512..efe2cc14 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/EnableFeignAutoRefresh.java @@ -3,12 +3,13 @@ import org.springframework.context.annotation.Import; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + /** * Enable Feign Auto Refresh * @@ -17,8 +18,8 @@ * @see FeignClientAutoRefreshAutoConfiguration * @since 0.0.1 */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE}) +@Retention(RUNTIME) +@Target(TYPE) @Documented @Inherited @Import(EnableFeignAutoRefresh.Marker.class) From 3b36ccce319aefa2b511ce93fe3cce4d5bcf2cc5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:48:13 +0800 Subject: [PATCH 046/310] Update ConditionalOnFeaturesEnabled.java --- .../cloud/client/condition/ConditionalOnFeaturesEnabled.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java index 5bf03040..1ce3f27c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java @@ -24,9 +24,7 @@ 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; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.FEATURES_ENABLED_PROPERTY_NAME; From 92dedff629cc5b11199a0fef87d8073b946894a3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:48:17 +0800 Subject: [PATCH 047/310] Update ConditionalOnAutoServiceRegistrationEnabled.java --- .../condition/ConditionalOnAutoServiceRegistrationEnabled.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java index 6112d717..0e800bdf 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java @@ -21,9 +21,7 @@ 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; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME; From 985d57fd283c81bbad569e6bc10b3f33e5f06d69 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 11:48:21 +0800 Subject: [PATCH 048/310] Update ConditionalOnMultipleRegistrationEnabled.java --- .../condition/ConditionalOnMultipleRegistrationEnabled.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java index f22f4998..7a3dce7e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java @@ -4,9 +4,7 @@ import org.springframework.context.annotation.Conditional; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME; From 5dc0348ab31540d3a8ec04e7317b9b33964282c6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 12:51:56 +0800 Subject: [PATCH 049/310] Refactor test classes to use JUnit 5 conventions Updated all test classes to use package-private class and method visibility, and replaced 'public' with default access for test methods. This aligns with JUnit 5 best practices and improves code clarity. --- .../ConditionalOnFeaturesEnabledTest.java | 4 ++-- .../DiscoveryClientAutoConfigurationTest.java | 4 ++-- .../UnionDiscoveryClientTest.java | 2 +- .../DiscoveryClientConstantsTest.java | 4 ++-- .../ServiceInstancesChangedEventTest.java | 8 ++++---- .../registry/MultipleServiceRegistryTest.java | 4 ++-- ...istrationEndpointAutoConfigurationTest.java | 4 ++-- .../ServiceRegistryAutoConfigurationTest.java | 4 ++-- ...rviceRegistrationAutoConfigurationTest.java | 4 ++-- ...vcServiceRegistryAutoConfigurationTest.java | 4 ++-- .../CommonsPropertyConstantsTest.java | 4 ++-- ...catFaultToleranceAutoConfigurationTest.java | 18 +++++++++--------- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java index a3f50dfd..6e57810c 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java @@ -43,7 +43,7 @@ "spring.cloud.features.enabled=true" } ) -public class ConditionalOnFeaturesEnabledTest { +class ConditionalOnFeaturesEnabledTest { @ConditionalOnFeaturesEnabled static class FeaturesConfiguration { @@ -53,7 +53,7 @@ static class FeaturesConfiguration { private ObjectProvider featuresConfigurationProvider; @Test - public void test() { + void test() { assertNotNull(featuresConfigurationProvider.getIfAvailable()); } } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java index a13bb348..e5092dd4 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java @@ -61,13 +61,13 @@ "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1" } ) -public class DiscoveryClientAutoConfigurationTest { +class DiscoveryClientAutoConfigurationTest { @Autowired private DiscoveryClient discoveryClient; @Test - public void test() { + void test() { assertEquals(CompositeDiscoveryClient.class, discoveryClient.getClass()); CompositeDiscoveryClient compositeDiscoveryClient = CompositeDiscoveryClient.class.cast(discoveryClient); List discoveryClients = compositeDiscoveryClient.getDiscoveryClients(); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java index 513a4a34..ff7807ab 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java @@ -63,7 +63,7 @@ public class UnionDiscoveryClientTest { private DiscoveryClient discoveryClient; @Test - public void test() { + void test() { assertEquals(CompositeDiscoveryClient.class, discoveryClient.getClass()); CompositeDiscoveryClient compositeDiscoveryClient = CompositeDiscoveryClient.class.cast(discoveryClient); List discoveryClients = compositeDiscoveryClient.getDiscoveryClients(); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java index 65851f68..5a9721c7 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java @@ -33,10 +33,10 @@ * @see DiscoveryClientConstants * @since 1.0.0 */ -public class DiscoveryClientConstantsTest { +class DiscoveryClientConstantsTest { @Test - public void testConstants() { + void testConstants() { assertEquals("microsphere.spring.cloud.client.discovery.", DISCOVERY_CLIENT_PROPERTY_PREFIX); assertEquals("mode", MODE_PROPERTY_NAME); assertEquals("union", UNION_DISCOVERY_CLIENT_MODE); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java index 74d33a2e..da3a7ca9 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEventTest.java @@ -21,7 +21,7 @@ * @see ServiceInstancesChangedEvent * @since 1.0.0 */ -public class ServiceInstancesChangedEventTest { +class ServiceInstancesChangedEventTest { private String serviceName = "testService"; @@ -46,20 +46,20 @@ private ServiceInstance createInstance(String serviceName) { } @Test - public void testGetServiceName() { + void testGetServiceName() { assertEquals(this.serviceName, this.event.getServiceName()); assertEquals(this.serviceName, this.event.getSource()); } @Test - public void testGetServiceInstances() { + void testGetServiceInstances() { assertEquals(Arrays.asList(this.instance), this.event.getServiceInstances()); assertEquals(this.instance, this.event.getServiceInstances().get(0)); assertSame(this.instance, this.event.getServiceInstances().get(0)); } @Test - public void testProcessed() { + void testProcessed() { assertFalse(this.event.isProcessed()); this.event.processed(); assertTrue(this.event.isProcessed()); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java index ee918731..17b9553d 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java @@ -88,7 +88,7 @@ public void onApplicationEvent(RegistrationPreRegisteredEvent event) { } @Test - public void test() throws Exception { + void test() throws Exception { assertNotNull(serviceRegistry); assertNotNull(registration); autoServiceRegistration.start(); @@ -98,7 +98,7 @@ public void test() throws Exception { } @Test - public void testMetaData() throws Exception { + void testMetaData() throws Exception { assertNotNull(registration); autoServiceRegistration.start(); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java index 528396dd..9e87e0cc 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java @@ -29,7 +29,7 @@ } ) @EnableAutoConfiguration -public class ServiceRegistrationEndpointAutoConfigurationTest { +class ServiceRegistrationEndpointAutoConfigurationTest { @Autowired private ObjectProvider serviceRegistrationEndpoint; @@ -38,7 +38,7 @@ public class ServiceRegistrationEndpointAutoConfigurationTest { private ObjectProvider serviceDeregistrationEndpoint; @Test - public void testEndpoints() { + void testEndpoints() { assertNotNull(serviceRegistrationEndpoint); } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java index 340e268e..28eaf1b9 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java @@ -43,7 +43,7 @@ } ) @EnableAspectJAutoProxy -public class ServiceRegistryAutoConfigurationTest { +class ServiceRegistryAutoConfigurationTest { @Autowired private ConfigurableApplicationContext context; @@ -64,7 +64,7 @@ void setUp() { } @Test - public void testEventPublishingRegistrationAspect() { + void testEventPublishingRegistrationAspect() { context.addApplicationListener(this::onApplicationEvent); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java index 76f60709..71d01bad 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java @@ -29,7 +29,7 @@ } ) @EnableAutoConfiguration -public class SimpleAutoServiceRegistrationAutoConfigurationTest { +class SimpleAutoServiceRegistrationAutoConfigurationTest { @Autowired private Registration registration; @@ -41,7 +41,7 @@ public class SimpleAutoServiceRegistrationAutoConfigurationTest { private SimpleAutoServiceRegistration simpleAutoServiceRegistration; @Test - public void test() { + void test() { assertEquals("test-service", registration.getServiceId()); assertNotNull(registration.getHost()); assertNotNull(registration.getPort()); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java index 5f24ed74..e0b49450 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java @@ -48,13 +48,13 @@ ) @EnableAutoConfiguration @EnableWebMvcExtension -public class WebMvcServiceRegistryAutoConfigurationTest { +class WebMvcServiceRegistryAutoConfigurationTest { @Autowired private Registration registration; @Test - public void test() { + void test() { Map metadata = registration.getMetadata(); assertNotNull(metadata.get(WEB_MAPPINGS_METADATA_NAME)); } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java index 3986340e..979ad63e 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstantsTest.java @@ -37,10 +37,10 @@ * @see CommonsPropertyConstants * @since 1.0.0 */ -public class CommonsPropertyConstantsTest { +class CommonsPropertyConstantsTest { @Test - public void testConstants() { + void testConstants() { assertEquals("spring.cloud.", SPRING_CLOUD_PROPERTY_PREFIX); assertEquals("spring.cloud.service-registry.", SERVICE_REGISTRY_PROPERTY_PREFIX); assertEquals("spring.cloud.service-registry.auto-registration.enabled", SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java index 3dcbf8de..ca00aea0 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java @@ -40,7 +40,7 @@ TomcatFaultToleranceAutoConfigurationTest.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @EnableAutoConfiguration -public class TomcatFaultToleranceAutoConfigurationTest { +class TomcatFaultToleranceAutoConfigurationTest { @Autowired private EnvironmentManager environmentManager; @@ -63,7 +63,7 @@ void before() { } @Test - public void testMinSpareThreads() { + void testMinSpareThreads() { // default assertEquals(10, protocol.getMinSpareThreads()); // changed @@ -72,7 +72,7 @@ public void testMinSpareThreads() { } @Test - public void testMaxThreads() { + void testMaxThreads() { // default assertEquals(200, protocol.getMaxThreads()); // changed @@ -81,7 +81,7 @@ public void testMaxThreads() { } @Test - public void testAcceptCount() { + void testAcceptCount() { // default assertEquals(100, protocol.getAcceptCount()); // changed @@ -90,7 +90,7 @@ public void testAcceptCount() { } @Test - public void testConnectionTimeout() { + void testConnectionTimeout() { // default assertEquals(60000, protocol.getConnectionTimeout()); // changed @@ -99,7 +99,7 @@ public void testConnectionTimeout() { } @Test - public void testMaxConnections() { + void testMaxConnections() { // default assertEquals(8192, protocol.getMaxConnections()); // changed @@ -108,7 +108,7 @@ public void testMaxConnections() { } @Test - public void testMaxHttpHeaderSize() { + void testMaxHttpHeaderSize() { // default assertEquals(8192, protocol.getMaxHttpHeaderSize()); // changed @@ -117,7 +117,7 @@ public void testMaxHttpHeaderSize() { } @Test - public void testMaxSwallowSize() { + void testMaxSwallowSize() { // default assertEquals(1024 * 1024 * 2, protocol.getMaxSwallowSize()); // changed @@ -126,7 +126,7 @@ public void testMaxSwallowSize() { } @Test - public void testMaxHttpFormPostSize() { + void testMaxHttpFormPostSize() { // default assertEquals(1024 * 1024 * 2, connector.getMaxPostSize()); // changed From 49a4dd962371062478d6417b73894320d9eade89 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 12:52:02 +0800 Subject: [PATCH 050/310] Exclude JUnit from testcontainers junit-jupiter dependency Added an exclusion for the JUnit dependency in the org.testcontainers:junit-jupiter test dependency to prevent conflicts or redundant inclusion of JUnit. --- microsphere-spring-cloud-commons/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index bcc2a392..a9e9c7fc 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -97,6 +97,12 @@ org.testcontainers junit-jupiter test + + + junit + junit + + From c8d796bdbd5d83ad6130d6458cb238a157021907 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 13:02:39 +0800 Subject: [PATCH 051/310] Refactor test classes to package-private visibility Changed test classes from public to package-private to limit their visibility within the package. Also changed the testInternal method in BaseTest from public to protected for better encapsulation. --- .../java/io/microsphere/spring/cloud/openfeign/BaseTest.java | 2 +- .../spring/cloud/openfeign/decoder/DecoderChangedTest.java | 2 +- .../spring/cloud/openfeign/encoder/EncoderChangedTest.java | 2 +- .../cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java | 2 +- .../openfeign/querymapencoder/QueryMapEncoderChangedTest.java | 2 +- .../requestInterceptor/RequestInterceptorChangedTest.java | 2 +- .../spring/cloud/openfeign/retryer/RetryerChangedTest.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java index 47baa70b..0913cbe6 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java @@ -72,7 +72,7 @@ public void replaceConfig() { } @Test - public void testInternal() { + protected void testInternal() { ObservableFeignInvocationHandler.componentAssert = loadFeignComponentAssert(); ObservableFeignInvocationHandler.expectComponentClass = beforeTestComponentClass(); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java index bbbb810c..37c476cb 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/decoder/DecoderChangedTest.java @@ -12,7 +12,7 @@ */ @SpringBootTest(classes = DecoderChangedTest.class) @EnableAutoConfiguration -public class DecoderChangedTest extends BaseTest { +class DecoderChangedTest extends BaseTest { @Override protected String afterTestComponentConfigKey() { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java index 1229021d..3a772a46 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/encoder/EncoderChangedTest.java @@ -12,7 +12,7 @@ */ @SpringBootTest(classes = EncoderChangedTest.class) @EnableAutoConfiguration -public class EncoderChangedTest extends BaseTest { +class EncoderChangedTest extends BaseTest { @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java index c0140792..5ee8d7ef 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/ErrorDecoderChangedTest.java @@ -12,7 +12,7 @@ */ @SpringBootTest(classes = ErrorDecoderChangedTest.class) @EnableAutoConfiguration -public class ErrorDecoderChangedTest extends BaseTest { +class ErrorDecoderChangedTest extends BaseTest { @Override protected Class beforeTestComponentClass() { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java index 1afa7dfd..1374594a 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/QueryMapEncoderChangedTest.java @@ -12,7 +12,7 @@ */ @SpringBootTest(classes = QueryMapEncoderChangedTest.class) @EnableAutoConfiguration -public class QueryMapEncoderChangedTest extends BaseTest { +class QueryMapEncoderChangedTest extends BaseTest { @Override protected Class beforeTestComponentClass() { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java index b290206a..bda755f6 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java @@ -12,7 +12,7 @@ */ @SpringBootTest(classes = RequestInterceptorChangedTest.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) @EnableAutoConfiguration -public class RequestInterceptorChangedTest extends BaseTest { +class RequestInterceptorChangedTest extends BaseTest { @Override protected Class beforeTestComponentClass() { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java index 1ac2c785..a983430a 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/RetryerChangedTest.java @@ -12,7 +12,7 @@ */ @SpringBootTest(classes = RetryerChangedTest.class) @EnableAutoConfiguration -public class RetryerChangedTest extends BaseTest { +class RetryerChangedTest extends BaseTest { @Override protected Class beforeTestComponentClass() { From 32e0b7d27400c9a829acb67918a45e75c3848098 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 13:16:27 +0800 Subject: [PATCH 052/310] Add logback-test.xml for test logging configuration Introduced logback-test.xml files in commons and openfeign test resources to standardize and enhance logging output during test execution. --- .../src/test/resources/logback-test.xml | 27 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/resources/logback-test.xml create mode 100644 microsphere-spring-cloud-openfeign/src/test/resources/logback-test.xml diff --git a/microsphere-spring-cloud-commons/src/test/resources/logback-test.xml b/microsphere-spring-cloud-commons/src/test/resources/logback-test.xml new file mode 100644 index 00000000..0d11a9ed --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + + ${ENCODER_PATTERN} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/test/resources/logback-test.xml b/microsphere-spring-cloud-openfeign/src/test/resources/logback-test.xml new file mode 100644 index 00000000..0d11a9ed --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + + ${ENCODER_PATTERN} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 41d31b6aa4902a461d4a2e63205c359ea83b3d53 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 15:35:23 +0800 Subject: [PATCH 053/310] Annotate property constants with @ConfigurationProperty #66 Added @ConfigurationProperty annotations to several property constants in CommonsPropertyConstants and FaultTolerancePropertyConstants. This provides metadata such as type, default value, and source for configuration properties, improving clarity and tooling support. --- .../constants/CommonsPropertyConstants.java | 20 +++++++++++++++++++ .../FaultTolerancePropertyConstants.java | 13 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java index 62082630..e0d6eb1e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java @@ -16,9 +16,11 @@ */ package io.microsphere.spring.cloud.commons.constants; +import io.microsphere.annotation.ConfigurationProperty; import org.springframework.cloud.client.CommonsClientAutoConfiguration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; +import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; import static io.microsphere.constants.PropertyConstants.ENABLED_PROPERTY_NAME; import static io.microsphere.constants.PropertyConstants.MICROSPHERE_PROPERTY_NAME_PREFIX; @@ -68,21 +70,39 @@ public interface CommonsPropertyConstants { /** * The property name for Multiple Service Registry Enabled Feature : "microsphere.spring.cloud.multiple-registration.enabled" */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "false", + source = APPLICATION_SOURCE + ) String MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "multiple-registration." + ENABLED_PROPERTY_NAME; /** * The property name for Default Service Registry Type : "microsphere.spring.cloud.default-registration.type" */ + @ConfigurationProperty( + type = Class.class, + source = APPLICATION_SOURCE + ) String MULTIPLE_REGISTRATION_DEFAULT_REGISTRATION_PROPERTY_NAME = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "default-registration.type"; /** * The property name for Default Service Registry Type : "microsphere.spring.cloud.default-service-registry.type" */ + @ConfigurationProperty( + type = Class.class, + source = APPLICATION_SOURCE + ) String MULTIPLE_REGISTRATION_DEFAULT_REGISTRY_PROPERTY_NAME = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "default-service-registry.type"; /** * The property name for Composite Service Registry Enabled Feature : "microsphere.spring.cloud.composite-registration.enabled" */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "false", + source = APPLICATION_SOURCE + ) String COMPOSITE_REGISTRATION_ENABLED_PROPERTY_NAME = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "composite-registration." + ENABLED_PROPERTY_NAME; } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java index c6071fe6..9be38f1a 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java @@ -1,7 +1,10 @@ package io.microsphere.spring.cloud.fault.tolerance.constants; +import io.microsphere.annotation.ConfigurationProperty; + import java.util.concurrent.TimeUnit; +import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX; /** @@ -35,11 +38,21 @@ public interface FaultTolerancePropertyConstants { /** * The metadata name of warm-up time */ + @ConfigurationProperty( + type = long.class, + defaultValue = "600000", + source = APPLICATION_SOURCE + ) String WARMUP_TIME_PROPERTY_NAME = FAULT_TOLERANCE_PROPERTY_NAME_PREFIX + "warmup-time"; /** * The property name of weight */ + @ConfigurationProperty( + type = int.class, + defaultValue = "100", + source = APPLICATION_SOURCE + ) String WEIGHT_PROPERTY_NAME = FAULT_TOLERANCE_PROPERTY_NAME_PREFIX + "weight"; /** From c0f00f5dffc98b95dd52b350118f7a5ae665ad16 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 15:39:42 +0800 Subject: [PATCH 054/310] Add @ConfigurationProperty annotations to constants #66 Annotated SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME and FEATURES_ENABLED_PROPERTY_NAME with @ConfigurationProperty to provide metadata such as type, default value, and source. This enhances property documentation and tooling support. --- .../commons/constants/CommonsPropertyConstants.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java index e0d6eb1e..99bb3132 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java @@ -48,6 +48,11 @@ public interface CommonsPropertyConstants { * * @see AutoServiceRegistrationAutoConfiguration */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "true", + source = APPLICATION_SOURCE + ) String SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME = SERVICE_REGISTRY_PROPERTY_PREFIX + "auto-registration." + ENABLED_PROPERTY_NAME; /** @@ -55,6 +60,11 @@ public interface CommonsPropertyConstants { * * @see CommonsClientAutoConfiguration.ActuatorConfiguration */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "true", + source = APPLICATION_SOURCE + ) String FEATURES_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "features." + ENABLED_PROPERTY_NAME; /** From a4b636027fca9534697b2f4370261838669a487f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 15:40:23 +0800 Subject: [PATCH 055/310] Update ConditionalOnMultipleRegistrationEnabled.java --- .../condition/ConditionalOnMultipleRegistrationEnabled.java | 1 - 1 file changed, 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java index 7a3dce7e..e25f3138 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java @@ -24,5 +24,4 @@ @Documented @ConditionalOnProperty(name = MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME) public @interface ConditionalOnMultipleRegistrationEnabled { - } From 69d41020bcb16ef944d13f717eb4a7f350fb34cd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 15:40:28 +0800 Subject: [PATCH 056/310] Update ConditionalOnMultipleRegistrationEnabled.java --- .../condition/ConditionalOnMultipleRegistrationEnabled.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java index e25f3138..7051e2ee 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnMultipleRegistrationEnabled.java @@ -24,4 +24,4 @@ @Documented @ConditionalOnProperty(name = MULTIPLE_REGISTRATION_ENABLED_PROPERTY_NAME) public @interface ConditionalOnMultipleRegistrationEnabled { -} +} \ No newline at end of file From 0567c5f59b1dcd1e65da8e8cad5872abff42858e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 15:40:34 +0800 Subject: [PATCH 057/310] Update ConditionalOnAutoServiceRegistrationEnabled.java --- .../condition/ConditionalOnAutoServiceRegistrationEnabled.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java index 0e800bdf..720d5f0e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/condition/ConditionalOnAutoServiceRegistrationEnabled.java @@ -52,4 +52,4 @@ */ @AliasFor(annotation = ConditionalOnProperty.class, attribute = "matchIfMissing") boolean matchIfMissing() default true; -} +} \ No newline at end of file From 6b87297670a8c985c28174ea3f767e289cce2e25 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 15:41:35 +0800 Subject: [PATCH 058/310] Add spacing between field declarations Added a blank line between @Value-injected fields for improved code readability in WebMvcServiceRegistryAutoConfiguration. --- .../autoconfigure/WebMvcServiceRegistryAutoConfiguration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index 79acc343..fbc03778 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -75,6 +75,7 @@ public class WebMvcServiceRegistryAutoConfiguration { @Value("${management.endpoints.web.base-path:/actuator}") private String actuatorBasePath; + @Value("${server.servlet.context-path:}") private String contextPath; From 894f8169400fa2edffa71ae0a00cc4863fa67d90 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 15:53:16 +0800 Subject: [PATCH 059/310] Refactor DiscoveryClient mode property constant usage #66 Replaces separate DISCOVERY_CLIENT_PROPERTY_PREFIX and MODE_PROPERTY_NAME usage with the new DISCOVERY_CLIENT_MODE_PROPERTY_NAME constant. Adds @ConfigurationProperty annotation to DISCOVERY_CLIENT_MODE_PROPERTY_NAME for better configuration metadata. --- .../DiscoveryClientAutoConfiguration.java | 5 ++--- .../discovery/constants/DiscoveryClientConstants.java | 10 ++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java index 25302cba..88e2290c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java @@ -30,8 +30,7 @@ import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; -import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_PROPERTY_PREFIX; -import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.MODE_PROPERTY_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_MODE_PROPERTY_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.UNION_DISCOVERY_CLIENT_MODE; /** @@ -54,7 +53,7 @@ public class DiscoveryClientAutoConfiguration { @Configuration(proxyBeanMethods = false) - @ConditionalOnProperty(prefix = DISCOVERY_CLIENT_PROPERTY_PREFIX, name = MODE_PROPERTY_NAME, havingValue = UNION_DISCOVERY_CLIENT_MODE) + @ConditionalOnProperty(name = DISCOVERY_CLIENT_MODE_PROPERTY_NAME, havingValue = UNION_DISCOVERY_CLIENT_MODE) public static class UnionConfiguration { @Bean diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java index e1fb94c0..e1a11086 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java @@ -16,11 +16,13 @@ */ package io.microsphere.spring.cloud.client.discovery.constants; +import io.microsphere.annotation.ConfigurationProperty; import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; import org.springframework.cloud.client.CommonsClientAutoConfiguration; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; +import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX; /** @@ -46,6 +48,14 @@ public interface DiscoveryClientConstants { */ String UNION_DISCOVERY_CLIENT_MODE = "union"; + /** + * The property name of DiscoveryClient mode : "microsphere.spring.cloud.client.discovery.mode" + */ + @ConfigurationProperty( + source = APPLICATION_SOURCE + ) + String DISCOVERY_CLIENT_MODE_PROPERTY_NAME = DISCOVERY_CLIENT_PROPERTY_PREFIX + MODE_PROPERTY_NAME; + /** * The class name of {@link DiscoveryClient} */ From cb8a33364d8bb48ddb08b73356b08649149877e9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 17:43:49 +0800 Subject: [PATCH 060/310] Extract SpringCloudPropertyConstants to new interface Moved Spring Cloud property constants from CommonsPropertyConstants to a new SpringCloudPropertyConstants interface. CommonsPropertyConstants now extends SpringCloudPropertyConstants, improving separation of concerns and code organization. --- .../constants/CommonsPropertyConstants.java | 41 +---------- .../SpringCloudPropertyConstants.java | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java index 99bb3132..b30fbe48 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/CommonsPropertyConstants.java @@ -17,8 +17,6 @@ package io.microsphere.spring.cloud.commons.constants; import io.microsphere.annotation.ConfigurationProperty; -import org.springframework.cloud.client.CommonsClientAutoConfiguration; -import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; import static io.microsphere.constants.PropertyConstants.ENABLED_PROPERTY_NAME; @@ -30,42 +28,7 @@ * @author Mercy * @since 1.0.0 */ -public interface CommonsPropertyConstants { - - /** - * The property name prefix of Spring Cloud properties : "spring.cloud." - */ - String SPRING_CLOUD_PROPERTY_PREFIX = "spring.cloud."; - - /** - * The property name prefix of Spring Cloud Service Registry : "spring.cloud.service-registry." - */ - String SERVICE_REGISTRY_PROPERTY_PREFIX = SPRING_CLOUD_PROPERTY_PREFIX + "service-registry."; - - /** - * The property name for Spring Cloud Service Registry Auto-Registration Feature : - * "spring.cloud.service-registry.auto-registration.enabled" - * - * @see AutoServiceRegistrationAutoConfiguration - */ - @ConfigurationProperty( - type = boolean.class, - defaultValue = "true", - source = APPLICATION_SOURCE - ) - String SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME = SERVICE_REGISTRY_PROPERTY_PREFIX + "auto-registration." + ENABLED_PROPERTY_NAME; - - /** - * The property name for enabling Spring Cloud Features : "spring.cloud.features.enabled" - * - * @see CommonsClientAutoConfiguration.ActuatorConfiguration - */ - @ConfigurationProperty( - type = boolean.class, - defaultValue = "true", - source = APPLICATION_SOURCE - ) - String FEATURES_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "features." + ENABLED_PROPERTY_NAME; +public interface CommonsPropertyConstants extends SpringCloudPropertyConstants { /** * The property name prefix of Microsphere Cloud : "microsphere.spring.cloud." @@ -115,4 +78,4 @@ public interface CommonsPropertyConstants { ) String COMPOSITE_REGISTRATION_ENABLED_PROPERTY_NAME = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "composite-registration." + ENABLED_PROPERTY_NAME; -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java new file mode 100644 index 00000000..17b793f0 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.commons.constants; + +import io.microsphere.annotation.ConfigurationProperty; +import org.springframework.cloud.client.CommonsClientAutoConfiguration; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; + +import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; +import static io.microsphere.constants.PropertyConstants.ENABLED_PROPERTY_NAME; + +/** + * The property constants for Spring Cloud + * + * @author Mercy + * @see AutoServiceRegistrationAutoConfiguration + * @see CommonsClientAutoConfiguration + * @since 1.0.0 + */ +public interface SpringCloudPropertyConstants { + + /** + * The property name prefix of Spring Cloud properties : "spring.cloud." + */ + String SPRING_CLOUD_PROPERTY_PREFIX = "spring.cloud."; + + /** + * The property name prefix of Spring Cloud Service Registry : "spring.cloud.service-registry." + */ + String SERVICE_REGISTRY_PROPERTY_PREFIX = SPRING_CLOUD_PROPERTY_PREFIX + "service-registry."; + + /** + * The property name for Spring Cloud Service Registry Auto-Registration Feature : + * "spring.cloud.service-registry.auto-registration.enabled" + * + * @see AutoServiceRegistrationAutoConfiguration + */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "true", + source = APPLICATION_SOURCE + ) + String SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME = SERVICE_REGISTRY_PROPERTY_PREFIX + "auto-registration." + ENABLED_PROPERTY_NAME; + + /** + * The property name for enabling Spring Cloud Features : "spring.cloud.features.enabled" + * + * @see CommonsClientAutoConfiguration.ActuatorConfiguration + */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "true", + source = APPLICATION_SOURCE + ) + String FEATURES_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "features." + ENABLED_PROPERTY_NAME; +} From fa1c9738a939ca1783ee6991f4236e97eac945fb Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 18:24:34 +0800 Subject: [PATCH 061/310] Refactor metadata constants and add unit tests #33 Moved MANAGEMENT_PORT_METADATA_NAME and START_TIME_METADATA_NAME from FaultTolerancePropertyConstants to InstanceConstants for better organization. Added unit tests for both InstanceConstants and FaultTolerancePropertyConstants to verify constant values. --- .../registry/constants/InstanceConstants.java | 10 ++++ .../FaultTolerancePropertyConstants.java | 10 ---- .../constants/InstanceConstantsTest.java | 45 +++++++++++++++++ .../FaultTolerancePropertyConstantsTest.java | 50 +++++++++++++++++++ 4 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstantsTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstantsTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java index d27e5587..7128f753 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java @@ -33,4 +33,14 @@ public interface InstanceConstants { * The meta-data name of Web Context Path */ String WEB_CONTEXT_PATH_METADATA_NAME = "web.context-path"; + + /** + * The metadata name of management + */ + String MANAGEMENT_PORT_METADATA_NAME = "management-port"; + + /** + * The metadata name of start time + */ + String START_TIME_METADATA_NAME = "start-time"; } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java index 9be38f1a..216cc000 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java @@ -25,16 +25,6 @@ public interface FaultTolerancePropertyConstants { */ String LOAD_BALANCER_PROPERTY_PREFIX = FAULT_TOLERANCE_PROPERTY_NAME_PREFIX + "load-balancer."; - /** - * The metadata name of management - */ - String MANAGEMENT_PORT_METADATA_NAME = "management-port"; - - /** - * The metadata name of start time - */ - String START_TIME_METADATA_NAME = "start-time"; - /** * The metadata name of warm-up time */ diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstantsTest.java new file mode 100644 index 00000000..42598211 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstantsTest.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 io.microsphere.spring.cloud.client.service.registry.constants; + + +import org.junit.jupiter.api.Test; + +import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.MANAGEMENT_PORT_METADATA_NAME; +import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.START_TIME_METADATA_NAME; +import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; +import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_MAPPINGS_METADATA_NAME; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link InstanceConstants} Test + * + * @author Mercy + * @see InstanceConstants + * @since 1.0.0 + */ +class InstanceConstantsTest { + + @Test + void testConstants() { + assertEquals("web.mappings", WEB_MAPPINGS_METADATA_NAME); + assertEquals("web.context-path", WEB_CONTEXT_PATH_METADATA_NAME); + assertEquals("management-port", MANAGEMENT_PORT_METADATA_NAME); + assertEquals("start-time", START_TIME_METADATA_NAME); + } +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstantsTest.java new file mode 100644 index 00000000..89c83ca7 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstantsTest.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.fault.tolerance.constants; + + +import org.junit.jupiter.api.Test; + +import static io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants.DEFAULT_WARMUP_TIME_PROPERTY_VALUE; +import static io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants.DEFAULT_WEIGHT_PROPERTY_VALUE; +import static io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants.FAULT_TOLERANCE_PROPERTY_NAME_PREFIX; +import static io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants.LOAD_BALANCER_PROPERTY_PREFIX; +import static io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants.WARMUP_TIME_PROPERTY_NAME; +import static io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants.WEIGHT_PROPERTY_NAME; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link FaultTolerancePropertyConstants} Test + * + * @author Mercy + * @see FaultTolerancePropertyConstants + * @since 1.0.0 + */ +class FaultTolerancePropertyConstantsTest { + + @Test + void testConstants() { + assertEquals("microsphere.spring.cloud.fault-tolerance.", FAULT_TOLERANCE_PROPERTY_NAME_PREFIX); + assertEquals("microsphere.spring.cloud.fault-tolerance.load-balancer.", LOAD_BALANCER_PROPERTY_PREFIX); + assertEquals("microsphere.spring.cloud.fault-tolerance.warmup-time", WARMUP_TIME_PROPERTY_NAME); + assertEquals("microsphere.spring.cloud.fault-tolerance.weight", WEIGHT_PROPERTY_NAME); + assertEquals(600000, DEFAULT_WARMUP_TIME_PROPERTY_VALUE); + assertEquals(100, DEFAULT_WEIGHT_PROPERTY_VALUE); + } + +} \ No newline at end of file From 86df10718f8ec15fb031a505a4b1c1f6427951d5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 20:30:43 +0800 Subject: [PATCH 062/310] Add getInstanceId to MultipleRegistration Implemented the getInstanceId method in MultipleRegistration to delegate to the default registration. Also replaced CollectionUtils.isEmpty with assertNotEmpty for input validation and made a minor code style improvement in the special() method. --- .../registry/MultipleRegistration.java | 13 ++- .../registry/MultipleRegistrationTest.java | 104 ++++++++++++++++++ 2 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java index 75d3fd7b..9b7ce348 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java @@ -1,13 +1,14 @@ package io.microsphere.spring.cloud.client.service.registry; import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.util.CollectionUtils; import java.net.URI; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import static io.microsphere.util.Assert.assertNotEmpty; + /** * The Delegating {@link Registration} for the multiple service registration * @@ -25,8 +26,7 @@ public class MultipleRegistration implements Registration { private final RegistrationMetaData metaData; public MultipleRegistration(Collection registrations) { - if (CollectionUtils.isEmpty(registrations)) - throw new IllegalArgumentException("registrations cannot be empty"); + assertNotEmpty(registrations, () -> "registrations cannot be empty"); //init map for (Registration registration : registrations) { Class clazz = registration.getClass(); @@ -36,6 +36,11 @@ public MultipleRegistration(Collection registrations) { this.metaData = new RegistrationMetaData(registrations); } + @Override + public String getInstanceId() { + return getDefaultRegistration().getInstanceId(); + } + @Override public String getServiceId() { return getDefaultRegistration().getServiceId(); @@ -71,7 +76,7 @@ public Registration getDefaultRegistration() { } public T special(Class specialClass) { - if (specialClass.equals(Registration.class)) + if (Registration.class.equals(specialClass)) return (T) this; return (T) this.registrationMap.getOrDefault(specialClass, null); } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java new file mode 100644 index 00000000..7340b60f --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.serviceregistry.Registration; + +import static io.microsphere.collection.Lists.ofList; +import static java.lang.System.currentTimeMillis; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link MultipleRegistration} Test + * + * @author Mercy + * @see MultipleRegistration + * @since 1.0.0 + */ +class MultipleRegistrationTest { + + private DefaultRegistration defaultRegistration; + + private MultipleRegistration registration; + + @BeforeEach + void setUp() { + this.defaultRegistration = createDefaultRegistration(); + this.registration = new MultipleRegistration(ofList(defaultRegistration)); + } + + private DefaultRegistration createDefaultRegistration() { + DefaultRegistration defaultRegistration = new DefaultRegistration(); + defaultRegistration.setInstanceId("ServiceInstance-" + currentTimeMillis()); + defaultRegistration.setServiceId("test-service"); + defaultRegistration.setHost("localhost"); + defaultRegistration.setPort(8080); + defaultRegistration.setSecure(false); + return defaultRegistration; + } + + @Test + void testGetInstanceId() { + assertEquals(defaultRegistration.getInstanceId(), registration.getInstanceId()); + } + + @Test + void testGetServiceId() { + assertEquals(defaultRegistration.getServiceId(), registration.getServiceId()); + } + + @Test + void testGetHost() { + assertEquals(defaultRegistration.getHost(), registration.getHost()); + } + + @Test + void testGetPort() { + assertEquals(defaultRegistration.getPort(), registration.getPort()); + } + + @Test + void testIsSecure() { + assertEquals(defaultRegistration.isSecure(), registration.isSecure()); + } + + @Test + void testGetUri() { + assertEquals(defaultRegistration.getUri(), registration.getUri()); + } + + @Test + void testGetMetadata() { + assertEquals(defaultRegistration.getMetadata(), registration.getMetadata()); + } + + @Test + void testGetDefaultRegistration() { + assertEquals(defaultRegistration, registration.getDefaultRegistration()); + } + + @Test + void testSpecial() { + assertSame(registration, registration.special(Registration.class)); + assertSame(defaultRegistration, registration.special(DefaultRegistration.class)); + } +} \ No newline at end of file From d82538344ebb9952ff87c8acbe0c9583dc75578a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 20:30:49 +0800 Subject: [PATCH 063/310] Use assertNotEmpty for registration validation Replaces manual empty check and exception with assertNotEmpty utility for validating the registrations collection in RegistrationMetaData. This improves code consistency and leverages existing assertion utilities. --- .../cloud/client/service/registry/RegistrationMetaData.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java index 8a35c2aa..0b9c2a85 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java @@ -9,6 +9,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import static io.microsphere.util.Assert.assertNotEmpty; + /** * @author 韩超 * @since 1.0.0 @@ -23,9 +25,7 @@ public final class RegistrationMetaData implements Map { private final Object lock = new Object(); public RegistrationMetaData(Collection registrations) { - if (CollectionUtils.isEmpty(registrations)) - throw new IllegalArgumentException("registrations cannot be empty"); - + assertNotEmpty(registrations, () -> "registrations cannot be empty"); this.registrations = registrations; this.applicationMetaData = new ConcurrentHashMap<>(); for (Registration registration : registrations) { From ef9be09af4e282e5441d4f3198c9f935ee7c4e97 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 21:13:45 +0800 Subject: [PATCH 064/310] Add RegistrationMetaDataTest and update method visibility #33 Added a new RegistrationMetaDataTest to cover RegistrationMetaData functionality. Changed createDefaultRegistration in MultipleRegistrationTest to static for reuse in the new test. --- .../registry/MultipleRegistrationTest.java | 2 +- .../registry/RegistrationMetaDataTest.java | 144 ++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java index 7340b60f..4c92a12d 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java @@ -46,7 +46,7 @@ void setUp() { this.registration = new MultipleRegistration(ofList(defaultRegistration)); } - private DefaultRegistration createDefaultRegistration() { + static DefaultRegistration createDefaultRegistration() { DefaultRegistration defaultRegistration = new DefaultRegistration(); defaultRegistration.setInstanceId("ServiceInstance-" + currentTimeMillis()); defaultRegistration.setServiceId("test-service"); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java new file mode 100644 index 00000000..6b4d8977 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.collection.MapUtils.ofEntry; +import static io.microsphere.collection.Maps.ofMap; +import static io.microsphere.spring.cloud.client.service.registry.MultipleRegistrationTest.createDefaultRegistration; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link RegistrationMetaData} Test + * + * @author Mercy + * @see RegistrationMetaData + * @since 1.0.0 + */ +class RegistrationMetaDataTest { + + private DefaultRegistration defaultRegistration; + + private RegistrationMetaData metaData; + + @BeforeEach + void setUp() { + this.defaultRegistration = createDefaultRegistration(); + Map metadata = this.defaultRegistration.getMetadata(); + metadata.put("key1", "value1"); + metadata.put("key2", "value2"); + metadata.put("key3", "value3"); + this.metaData = new RegistrationMetaData(ofList(defaultRegistration)); + } + + @Test + void testSize() { + assertEquals(3, metaData.size()); + } + + @Test + void testIsEmpty() { + assertEquals(false, metaData.isEmpty()); + } + + @Test + void testContainsKey() { + assertTrue(metaData.containsKey("key1")); + assertFalse(metaData.containsKey("key4")); + } + + @Test + void testContainsValue() { + assertTrue(metaData.containsValue("value1")); + assertFalse(metaData.containsValue("value4")); + } + + @Test + void testGet() { + assertEquals("value1", metaData.get("key1")); + assertEquals("value2", metaData.get("key2")); + assertEquals("value3", metaData.get("key3")); + assertNull(metaData.get("key4")); + } + + @Test + void testPut() { + metaData.put("key4", "value4"); + assertEquals("value4", metaData.get("key4")); + } + + @Test + void testRemove() { + metaData.remove("key1"); + assertNull(metaData.get("key1")); + } + + @Test + void testPutAll() { + metaData.putAll(ofMap("key4", "value4", "key5", "value5")); + assertEquals("value1", metaData.get("key1")); + assertEquals("value2", metaData.get("key2")); + assertEquals("value3", metaData.get("key3")); + assertEquals("value4", metaData.get("key4")); + assertEquals("value5", metaData.get("key5")); + } + + @Test + void testClear() { + metaData.clear(); + assertEquals(0, metaData.size()); + } + + @Test + void testKeySet() { + Set keys = metaData.keySet(); + assertTrue(keys.contains("key1")); + assertTrue(keys.contains("key2")); + assertTrue(keys.contains("key3")); + assertFalse(keys.contains("key4")); + } + + @Test + void testValues() { + Collection values = metaData.values(); + assertTrue(values.contains("value1")); + assertTrue(values.contains("value2")); + assertTrue(values.contains("value3")); + assertFalse(values.contains("value4")); + } + + @Test + void testEntrySet() { + Set> entries = metaData.entrySet(); + assertTrue(entries.contains(ofEntry("key1", "value1"))); + assertTrue(entries.contains(ofEntry("key2", "value2"))); + assertTrue(entries.contains(ofEntry("key3", "value3"))); + assertFalse(entries.contains(ofEntry("key4", "value4"))); + } +} \ No newline at end of file From 75f5f2ff594c39da22173d73bc7c1774f215d986 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 21:16:09 +0800 Subject: [PATCH 065/310] Refactor logger initialization to use getLogger utility Replaces direct usage of LoggerFactory.getLogger with the getLogger utility method across several classes for consistency and potential future flexibility. No functional changes to logging behavior. --- .../autoconfigure/WebMvcServiceRegistryAutoConfiguration.java | 3 ++- .../registry/endpoint/ServiceDeregistrationEndpoint.java | 4 +++- .../registry/endpoint/ServiceRegistrationEndpoint.java | 4 +++- .../cloud/client/service/util/ServiceInstanceUtils.java | 4 ++-- .../tomcat/event/TomcatDynamicConfigurationListener.java | 3 ++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index fbc03778..7948e11b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -43,6 +43,7 @@ import java.util.Objects; import java.util.Set; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; @@ -66,7 +67,7 @@ }) public class WebMvcServiceRegistryAutoConfiguration { - private static final Logger logger = LoggerFactory.getLogger(WebMvcServiceRegistryAutoConfiguration.class); + private static final Logger logger = getLogger(WebMvcServiceRegistryAutoConfiguration.class); private static final String[] DEFAULT_URL_MAPPINGS = {"/*"}; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java index f1dbdf72..ea74aaa4 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java @@ -6,6 +6,8 @@ import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration; +import static io.microsphere.logging.LoggerFactory.getLogger; + /** * The {@link Endpoint @Endpoint} for Service Deregistration * @@ -17,7 +19,7 @@ @Endpoint(id = "serviceDeregistration") public class ServiceDeregistrationEndpoint extends AbstractServiceRegistrationEndpoint { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = getLogger(getClass()); @WriteOperation public boolean stop() { diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java index 360f85bb..05924ce2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java @@ -12,6 +12,8 @@ import java.util.LinkedHashMap; import java.util.Map; +import static io.microsphere.logging.LoggerFactory.getLogger; + /** * The {@link Endpoint @Endpoint} for Service Registration * @@ -23,7 +25,7 @@ @Endpoint(id = "serviceRegistration") public class ServiceRegistrationEndpoint extends AbstractServiceRegistrationEndpoint { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = getLogger(getClass()); @ReadOperation public Map metadata() { diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index af21b9f1..c30aa13b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import io.microsphere.spring.web.metadata.WebEndpointMapping; import io.microsphere.util.BaseUtils; import org.springframework.cloud.client.ServiceInstance; @@ -30,6 +29,7 @@ import java.util.Map; import java.util.StringJoiner; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.net.URLUtils.decode; import static io.microsphere.net.URLUtils.encode; import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; @@ -43,7 +43,7 @@ */ public class ServiceInstanceUtils extends BaseUtils { - private static final Logger logger = LoggerFactory.getLogger(ServiceInstanceUtils.class); + private static final Logger logger = getLogger(ServiceInstanceUtils.class); public static void attachMetadata(String contextPath, ServiceInstance serviceInstance, Collection webEndpointMappings) { Map metadata = serviceInstance.getMetadata(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java index 21974029..968f55c0 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java @@ -37,6 +37,7 @@ import java.util.Set; import java.util.stream.Collectors; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.spring.boot.context.properties.bind.util.BindUtils.bind; import static io.microsphere.spring.core.env.EnvironmentUtils.getProperties; import static io.microsphere.spring.core.env.PropertySourcesUtils.getSubProperties; @@ -51,7 +52,7 @@ */ public class TomcatDynamicConfigurationListener implements ApplicationListener { - private static final Logger logger = LoggerFactory.getLogger(TomcatDynamicConfigurationListener.class); + private static final Logger logger = getLogger(TomcatDynamicConfigurationListener.class); private static final String SERVER_PROPERTIES_PREFIX = "server"; From 8b25a6bf26442e8fe116e31fbd224363c7d8f536 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 22:52:18 +0800 Subject: [PATCH 066/310] Refactor ServiceInstanceUtils for custom JSON handling Replaces Jackson-based JSON serialization/deserialization with custom logic using microsphere JSON utilities for WebEndpointMapping metadata. Adds helper methods for parsing and serializing WebEndpointMapping objects, and introduces getMetadata for retrieving metadata values. This change improves control over JSON structure and removes dependency on Jackson. --- .../service/util/ServiceInstanceUtils.java | 117 +++++++++++++++--- 1 file changed, 97 insertions(+), 20 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index c30aa13b..ad5628e5 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -16,24 +16,39 @@ */ package io.microsphere.spring.cloud.client.service.util; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import io.microsphere.annotation.Nonnull; +import io.microsphere.annotation.Nullable; +import io.microsphere.json.JSONArray; +import io.microsphere.json.JSONObject; import io.microsphere.logging.Logger; import io.microsphere.spring.web.metadata.WebEndpointMapping; +import io.microsphere.spring.web.metadata.WebEndpointMapping.Builder; import io.microsphere.util.BaseUtils; import org.springframework.cloud.client.ServiceInstance; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.StringJoiner; +import static io.microsphere.collection.ListUtils.newArrayList; +import static io.microsphere.constants.SeparatorConstants.LINE_SEPARATOR; +import static io.microsphere.constants.SymbolConstants.COLON; +import static io.microsphere.constants.SymbolConstants.COMMA; +import static io.microsphere.constants.SymbolConstants.DOUBLE_QUOTE; +import static io.microsphere.constants.SymbolConstants.RIGHT_CURLY_BRACE; +import static io.microsphere.json.JSONUtils.jsonArray; +import static io.microsphere.json.JSONUtils.readArray; import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.net.URLUtils.decode; import static io.microsphere.net.URLUtils.encode; import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_MAPPINGS_METADATA_NAME; +import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.valueOf; +import static io.microsphere.spring.web.metadata.WebEndpointMapping.of; +import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; +import static io.microsphere.util.StringUtils.isBlank; +import static java.util.Collections.emptyList; /** * {@link ServiceInstance} Utilities class @@ -48,31 +63,93 @@ public class ServiceInstanceUtils extends BaseUtils { public static void attachMetadata(String contextPath, ServiceInstance serviceInstance, Collection webEndpointMappings) { Map metadata = serviceInstance.getMetadata(); StringJoiner jsonBuilder = new StringJoiner(",", "[", "]"); - webEndpointMappings.stream().map(WebEndpointMapping::toJSON).forEach(jsonBuilder::add); + webEndpointMappings.stream().map(mapping -> toJSON(mapping)).forEach(jsonBuilder::add); String json = jsonBuilder.toString(); metadata.put(WEB_CONTEXT_PATH_METADATA_NAME, contextPath); - try { - String encodedJson = encode(json); - metadata.put(WEB_MAPPINGS_METADATA_NAME, encodedJson); - } catch (IllegalArgumentException e) { - logger.error("The JSON content of WebEndpointMappings can't be encoded : {}", json, e); - } + String encodedJson = encode(json); + metadata.put(WEB_MAPPINGS_METADATA_NAME, encodedJson); } + /** + * Get {@link WebEndpointMapping}s from {@link ServiceInstance} + * + * @param serviceInstance {@link ServiceInstance} + * @return {@link WebEndpointMapping}s + */ + @Nonnull public static Collection getWebEndpointMappings(ServiceInstance serviceInstance) { - List webEndpointMappings = Collections.emptyList(); Map metadata = serviceInstance.getMetadata(); String encodedJSON = metadata.get(WEB_MAPPINGS_METADATA_NAME); - if (encodedJSON != null) { - try { - String json = decode(encodedJSON); - ObjectMapper objectMapper = new ObjectMapper(); - webEndpointMappings = objectMapper.readValue(json, new TypeReference>() { - }); - } catch (Throwable e) { - logger.error("The encoded JSON content of WebEndpointMappings can't be parsed : {}", encodedJSON, e); - } + return parseWebEndpointMappings(encodedJSON); + } + + static String toJSON(WebEndpointMapping webEndpointMapping) { + // FIXME : Issue on WebEndpointMapping.toJSON() + String json = webEndpointMapping.toJSON(); + StringBuilder jsonBuilder = new StringBuilder(json); + int startIndex = jsonBuilder.lastIndexOf(LINE_SEPARATOR); + int endIndex = jsonBuilder.indexOf(RIGHT_CURLY_BRACE); + String kindItem = COMMA + LINE_SEPARATOR + DOUBLE_QUOTE + "kind" + DOUBLE_QUOTE + COLON + + DOUBLE_QUOTE + webEndpointMapping.getKind() + DOUBLE_QUOTE + LINE_SEPARATOR; + jsonBuilder.replace(startIndex, endIndex, kindItem); + return jsonBuilder.toString(); + } + + static List parseWebEndpointMappings(String encodedJSON) { + if (isBlank(encodedJSON)) { + return emptyList(); + } + String json = decode(encodedJSON); + JSONArray jsonArray = jsonArray(json); + int size = jsonArray.length(); + List webEndpointMappings = newArrayList(size); + for (int i = 0; i < size; i++) { + JSONObject jsonObject = jsonArray.optJSONObject(i); + WebEndpointMapping webEndpointMapping = parseWebEndpointMapping(jsonObject); + webEndpointMappings.add(webEndpointMapping); } return webEndpointMappings; } + + static WebEndpointMapping parseWebEndpointMapping(JSONObject jsonObject) { + String kind = jsonObject.optString("kind"); + int id = jsonObject.optInt("id"); + boolean negated = jsonObject.optBoolean("negated"); + String[] patterns = getArray(jsonObject, "patterns"); + String[] methods = getArray(jsonObject, "methods"); + String[] params = getArray(jsonObject, "params"); + String[] headers = getArray(jsonObject, "headers"); + String[] consumes = getArray(jsonObject, "consumes"); + String[] produces = getArray(jsonObject, "produces"); + Builder builder = of(valueOf(kind)) + .endpoint(Integer.valueOf(id)) + .patterns(patterns) + .methods(methods) + .params(params) + .headers(headers) + .consumes(consumes) + .produces(produces); + if (negated) { + builder.negate(); + } + return builder.build(); + } + + static String[] getArray(JSONObject jsonObject, String name) { + JSONArray jsonArray = jsonObject.optJSONArray(name); + return jsonArray == null ? EMPTY_STRING_ARRAY : readArray(jsonArray, String.class); + } + + /** + * Get metadata by metadataName + * + * @param serviceInstance {@link ServiceInstance} + * @param metadataName metadataName + * @return metadata value + */ + @Nullable + public static String getMetadata(ServiceInstance serviceInstance, String metadataName) { + Map metadata = serviceInstance.getMetadata(); + return metadata.get(metadataName); + } } From 7d7546c489358d78f25c428bba8ab73c6268ecd1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 22:52:23 +0800 Subject: [PATCH 067/310] Add unit tests for ServiceInstanceUtils Introduces ServiceInstanceUtilsTest to verify metadata attachment, web endpoint mapping parsing, and related utility methods. Ensures correct behavior for attaching and retrieving metadata, as well as parsing web endpoint mappings from JSON. --- .../util/ServiceInstanceUtilsTest.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java new file mode 100644 index 00000000..94a83314 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.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 io.microsphere.spring.cloud.client.service.util; + + +import io.microsphere.json.JSONObject; +import io.microsphere.spring.web.metadata.WebEndpointMapping; +import io.microsphere.spring.web.metadata.WebEndpointMapping.Builder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; + +import java.util.Collection; + +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.json.JSONUtils.jsonObject; +import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getMetadata; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMapping; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMappings; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.toJSON; +import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.SERVLET; +import static io.microsphere.spring.web.metadata.WebEndpointMapping.servlet; +import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; +import static java.lang.System.currentTimeMillis; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ServiceInstanceUtils} Test + * + * @author Mercy + * @see ServiceInstanceUtils + * @since 1.0.0 + */ +class ServiceInstanceUtilsTest { + + private static final Integer WEB_ENDPOINT_MAPPING_ID = Integer.valueOf(12345); + + private static final String WEB_ENDPOINT_MAPPING_URL_PATTERN = "/test"; + + private static final String WEB_ENDPOINT_MAPPING_METHOD = "GET"; + + private String context = "/"; + + private ServiceInstance serviceInstance; + + private Collection webEndpointMappings; + + @BeforeEach + void setUp() { + this.serviceInstance = createDefaultServiceInstance(); + this.webEndpointMappings = createWebEndpointMappings(); + } + + @Test + void testAttachMetadata() { + attachMetadata(this.context, this.serviceInstance, this.webEndpointMappings); + assertEquals(this.context, getMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + } + + @Test + void testGetWebEndpointMappings() { + attachMetadata(this.context, this.serviceInstance, this.webEndpointMappings); + Collection webEndpointMappings = getWebEndpointMappings(this.serviceInstance); + assertEquals(1, webEndpointMappings.size()); + WebEndpointMapping webEndpointMapping = webEndpointMappings.iterator().next(); + assertEquals(WEB_ENDPOINT_MAPPING_ID, webEndpointMapping.getId()); + assertTrue(webEndpointMapping.isNegated()); + assertEquals(WEB_ENDPOINT_MAPPING_URL_PATTERN, webEndpointMapping.getPatterns()[0]); + assertEquals(WEB_ENDPOINT_MAPPING_METHOD, webEndpointMapping.getMethods()[0]); + assertEquals(SERVLET, webEndpointMapping.getKind()); + assertArrayEquals(EMPTY_STRING_ARRAY, webEndpointMapping.getParams()); + assertArrayEquals(EMPTY_STRING_ARRAY, webEndpointMapping.getHeaders()); + assertArrayEquals(EMPTY_STRING_ARRAY, webEndpointMapping.getProduces()); + assertArrayEquals(EMPTY_STRING_ARRAY, webEndpointMapping.getConsumes()); + } + + @Test + void testParseWebEndpointMappings() { + assertSame(emptyList(), parseWebEndpointMappings(null)); + assertSame(emptyList(), parseWebEndpointMappings("")); + assertSame(emptyList(), parseWebEndpointMappings(" ")); + } + + @Test + void testParseWebEndpointMapping() { + WebEndpointMapping webEndpointMapping = buildWebEndpointMapping(false); + String json = toJSON(webEndpointMapping); + JSONObject jsonObject = jsonObject(json); + WebEndpointMapping webEndpointMapping1 = parseWebEndpointMapping(jsonObject); + assertEquals(webEndpointMapping, webEndpointMapping1); + } + + private Collection createWebEndpointMappings() { + return ofList(buildWebEndpointMapping(true)); + } + + private WebEndpointMapping buildWebEndpointMapping(boolean nagated) { + Builder builder = servlet() + .endpoint(WEB_ENDPOINT_MAPPING_ID) + .method(WEB_ENDPOINT_MAPPING_METHOD) + .pattern(WEB_ENDPOINT_MAPPING_URL_PATTERN); + if (nagated) { + builder.negate(); + } + return builder.build(); + } + + public static DefaultServiceInstance createDefaultServiceInstance() { + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setInstanceId("ServiceInstance-" + currentTimeMillis()); + serviceInstance.setServiceId("test-service"); + serviceInstance.setHost("localhost"); + serviceInstance.setPort(8080); + serviceInstance.setSecure(false); + return serviceInstance; + } +} \ No newline at end of file From 9414f74b732a57b2f973243be03b95b61fff9abc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 14 Oct 2025 22:57:13 +0800 Subject: [PATCH 068/310] Add private constructor to ServiceInstanceUtils Introduced a private constructor to prevent instantiation of the ServiceInstanceUtils utility class. --- .../spring/cloud/client/service/util/ServiceInstanceUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index ad5628e5..ebbc4f2e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -152,4 +152,7 @@ public static String getMetadata(ServiceInstance serviceInstance, String metadat Map metadata = serviceInstance.getMetadata(); return metadata.get(metadataName); } + + private ServiceInstanceUtils() { + } } From 07187fe7ec53431afcca54e39102eef54bcbd8f3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 11:06:48 +0800 Subject: [PATCH 069/310] Format MultipleAutoServiceRegistration imports Added a blank line between field declarations in MultipleAutoServiceRegistration for improved code readability. --- .../client/service/registry/MultipleAutoServiceRegistration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java index 3d860b2b..901d3c2b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java @@ -17,6 +17,7 @@ public class MultipleAutoServiceRegistration extends AbstractAutoServiceRegistration { private final AutoServiceRegistrationProperties autoServiceRegistrationProperties; + private final MultipleRegistration multipleRegistration; public MultipleAutoServiceRegistration(MultipleRegistration multipleRegistration, From b9cfe29994c378b868392e62a905a52ead1e32bd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 11:06:52 +0800 Subject: [PATCH 070/310] Add unit test for MultipleAutoServiceRegistration Introduces MultipleAutoServiceRegistrationTest to verify the behavior of MultipleAutoServiceRegistration, including configuration retrieval, enablement, and registration access methods. --- .../MultipleAutoServiceRegistrationTest.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.java new file mode 100644 index 00000000..0bb5163b --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.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 io.microsphere.spring.cloud.client.service.registry; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.spring.cloud.client.service.registry.MultipleRegistrationTest.createDefaultRegistration; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link MultipleAutoServiceRegistration} Test + * + * @author Mercy + * @see MultipleAutoServiceRegistration + * @since 1.0.0 + */ +class MultipleAutoServiceRegistrationTest { + + private DefaultRegistration defaultRegistration; + + private MultipleRegistration registration; + + private ServiceRegistry serviceRegistry; + + private AutoServiceRegistrationProperties properties; + + private MultipleAutoServiceRegistration autoServiceRegistration; + + @BeforeEach + void setUp() { + this.defaultRegistration = createDefaultRegistration(); + this.registration = new MultipleRegistration(ofList(defaultRegistration)); + this.serviceRegistry = new InMemoryServiceRegistry(); + this.properties = new AutoServiceRegistrationProperties(); + this.autoServiceRegistration = new MultipleAutoServiceRegistration(registration, serviceRegistry, properties); + } + + @Test + void testGetConfiguration() { + assertNull(this.autoServiceRegistration.getConfiguration()); + } + + @Test + void testIsEnabled() { + assertEquals(this.properties.isEnabled(), this.autoServiceRegistration.isEnabled()); + } + + @Test + void testGetRegistration() { + assertSame(this.registration, this.autoServiceRegistration.getRegistration()); + } + + @Test + void testGetManagementRegistration() { + assertSame(this.registration, this.autoServiceRegistration.getManagementRegistration()); + } +} \ No newline at end of file From 011d5d393a8a486da27d49bb8ce6fb12e2f6aa72 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 14:46:08 +0800 Subject: [PATCH 071/310] Fix null check in getMetadata method Corrects the null check in getMetadata by verifying the retrieved instance instead of the storage map, ensuring metadata is only accessed for existing registrations. --- .../cloud/client/service/registry/InMemoryServiceRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java index b094c8dc..d3191d8e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java @@ -72,7 +72,7 @@ public Object getStatus(Registration registration) { protected Map getMetadata(Registration registration) { String id = registration.getInstanceId(); Registration instance = storage.get(id); - if (storage != null) { + if (instance != null) { return instance.getMetadata(); } return null; From b50b4eb958e8f32bf39b0968f60761b09e4bfa7c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 14:46:18 +0800 Subject: [PATCH 072/310] Refactor MultipleServiceRegistry imports and assertions Replaces manual empty check with assertNotEmpty utility and optimizes static imports for AOP and ResolvableType utilities. Cleans up unused imports and improves code readability. --- .../registry/MultipleServiceRegistry.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java index ffaf3f45..ea794141 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java @@ -1,11 +1,7 @@ package io.microsphere.spring.cloud.client.service.registry; -import org.springframework.aop.framework.AopProxyUtils; -import org.springframework.aop.support.AopUtils; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.core.ResolvableType; -import org.springframework.util.CollectionUtils; import java.util.HashMap; import java.util.List; @@ -13,6 +9,10 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; +import static io.microsphere.util.Assert.assertNotEmpty; +import static org.springframework.aop.framework.AopProxyUtils.ultimateTargetClass; +import static org.springframework.aop.support.AopUtils.isAopProxy; +import static org.springframework.core.ResolvableType.forClass; import static org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames; import static org.springframework.util.ClassUtils.resolveClassName; @@ -35,12 +35,11 @@ public class MultipleServiceRegistry implements ServiceRegistry> beanNameToRegistrationTypesMap; private ServiceRegistry defaultServiceRegistry; + private String defaultRegistrationBeanName; public MultipleServiceRegistry(Map registriesMap) { - if (CollectionUtils.isEmpty(registriesMap)) { - throw new IllegalArgumentException("service registry cannot be empty"); - } + assertNotEmpty(registriesMap, () -> "registrations cannot be empty"); this.registriesMap = registriesMap; this.beanNameToRegistrationTypesMap = new HashMap<>(registriesMap.size()); @@ -97,7 +96,7 @@ public T getStatus(MultipleRegistration registration) { } private static Class getRegistrationClass(Class serviceRegistryClass, ServiceRegistry serviceRegistry) { - Class registrationClass = ResolvableType.forClass(serviceRegistryClass) + Class registrationClass = forClass(serviceRegistryClass) .as(ServiceRegistry.class) .getGeneric(0) .resolve(); @@ -107,8 +106,8 @@ private static Class getRegistrationClass(Class registrationClassNames; - if (AopUtils.isAopProxy(serviceRegistry)) { - registrationClassNames = loadFactoryNames(AopProxyUtils.ultimateTargetClass(serviceRegistry), classLoader); + if (isAopProxy(serviceRegistry)) { + registrationClassNames = loadFactoryNames(ultimateTargetClass(serviceRegistry), classLoader); } else { registrationClassNames = loadFactoryNames(serviceRegistryClass, classLoader); } From e8a2ac7fca2ac7af5f909eb1a550ff2345cd10a7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 14:46:28 +0800 Subject: [PATCH 073/310] Add integration test for MultipleServiceRegistry Introduces MultipleServiceRegistryIntegrationTest to verify integration and metadata synchronization for multiple service registries, including Nacos and Eureka, in a Spring Cloud environment. --- ...ultipleServiceRegistryIntegrationTest.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java new file mode 100644 index 00000000..ca33c082 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java @@ -0,0 +1,119 @@ +package io.microsphere.spring.cloud.client.service.registry; + +import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration; +import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration; +import com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration; +import com.netflix.appinfo.ApplicationInfoManager; +import com.netflix.appinfo.InstanceInfo; +import io.microsphere.spring.cloud.client.service.registry.autoconfigure.ServiceRegistryAutoConfiguration; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationPreRegisteredEvent; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.cloud.client.CommonsClientAutoConfiguration; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; +import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration; +import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; +import org.springframework.context.ApplicationListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * {@link MultipleServiceRegistry} Integration Test + * + * @author 韩超 + * @author Mercy + * @see MultipleServiceRegistry + * @since 1.0.0 + */ +@Disabled +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + AutoServiceRegistrationAutoConfiguration.class, + CommonsClientAutoConfiguration.class, + EurekaClientAutoConfiguration.class, + DiscoveryClientOptionalArgsConfiguration.class, + NacosServiceRegistryAutoConfiguration.class, + NacosServiceAutoConfiguration.class, + NacosDiscoveryAutoConfiguration.class, + UtilAutoConfiguration.class, + MultipleServiceRegistryIntegrationTest.class, + ServiceRegistryAutoConfiguration.class, +}) +@TestPropertySource( + properties = { + "spring.application.name=test", + "microsphere.spring.cloud.multiple-registration.enabled=true", + "microsphere.spring.cloud.default-registration.type=com.alibaba.cloud.nacos.registry.NacosRegistration", + "microsphere.spring.cloud.default-service-registry.type=com.alibaba.cloud.nacos.registry.NacosServiceRegistry", + "spring.cloud.service-registry.auto-registration.enabled=true", + "spring.cloud.nacos.discovery.namespace=f7ad23e0-f581-4516-9420-8c50aa6a7b89", + "spring.cloud.nacos.discovery.metadata.key=value", + "eureka.client.service-url.defaultZone=http://127.0.0.1:8080/eureka", + } +) +@EnableAutoConfiguration +class MultipleServiceRegistryIntegrationTest implements ApplicationListener { + + @Autowired + private ServiceRegistry serviceRegistry; + + @Autowired + private AutoServiceRegistrationProperties properties; + + @Autowired + private Registration registration; + + @Autowired + private MultipleAutoServiceRegistration autoServiceRegistration; + + @Override + public void onApplicationEvent(RegistrationPreRegisteredEvent event) { + this.registration.getMetadata().put("my-key", "my-value"); + if (event.getRegistration() instanceof EurekaRegistration) { + EurekaRegistration eurekaRegistration = (EurekaRegistration) event.getRegistration(); + ApplicationInfoManager applicationInfoManager = eurekaRegistration.getApplicationInfoManager(); + InstanceInfo instanceInfo = applicationInfoManager.getInfo(); + Map metadata = registration.getMetadata(); + // Sync metadata from Registration to InstanceInfo + instanceInfo.getMetadata().putAll(metadata); + } + } + + @Test + void test() throws Exception { + assertNotNull(serviceRegistry); + assertNotNull(registration); + autoServiceRegistration.start(); + Thread.sleep(60 * 1000); + + autoServiceRegistration.stop(); + } + + @Test + void testMetaData() throws Exception { + assertNotNull(registration); + + autoServiceRegistration.start(); + + assertEquals(registration.getMetadata().get("my-key"), "my-value"); + Thread.sleep(60 * 1000); + + autoServiceRegistration.stop(); + } + + +} \ No newline at end of file From c04a6a225c26c2493b01212196b12a7078866326 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 14:46:37 +0800 Subject: [PATCH 074/310] Refactor MultipleServiceRegistryTest for simplicity Simplifies the MultipleServiceRegistryTest by removing Spring context, auto-configuration, and external dependencies. The test now uses direct instantiation and focuses on core MultipleServiceRegistry functionality, improving maintainability and clarity. --- .../registry/MultipleServiceRegistryTest.java | 149 +++++++----------- 1 file changed, 58 insertions(+), 91 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java index 17b9553d..30002e45 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java @@ -1,113 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package io.microsphere.spring.cloud.client.service.registry; -import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration; -import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration; -import com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration; -import com.netflix.appinfo.ApplicationInfoManager; -import com.netflix.appinfo.InstanceInfo; -import io.microsphere.spring.cloud.client.service.registry.autoconfigure.ServiceRegistryAutoConfiguration; -import io.microsphere.spring.cloud.client.service.registry.event.RegistrationPreRegisteredEvent; -import org.junit.jupiter.api.Disabled; + +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.client.CommonsClientAutoConfiguration; -import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; -import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; -import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.cloud.commons.util.UtilAutoConfiguration; -import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; -import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration; -import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; -import org.springframework.context.ApplicationListener; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.Map; +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.collection.Maps.ofMap; +import static io.microsphere.spring.cloud.client.service.registry.MultipleRegistrationTest.createDefaultRegistration; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Disabled -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = { - AutoServiceRegistrationAutoConfiguration.class, - CommonsClientAutoConfiguration.class, - EurekaClientAutoConfiguration.class, - DiscoveryClientOptionalArgsConfiguration.class, - NacosServiceRegistryAutoConfiguration.class, - NacosServiceAutoConfiguration.class, - NacosDiscoveryAutoConfiguration.class, - UtilAutoConfiguration.class, - MultipleServiceRegistryTest.class, - ServiceRegistryAutoConfiguration.class, -}) -@TestPropertySource( - properties = { - "spring.application.name=test", - "microsphere.spring.cloud.multiple-registration.enabled=true", - "microsphere.spring.cloud.default-registration.type=com.alibaba.cloud.nacos.registry.NacosRegistration", - "microsphere.spring.cloud.default-service-registry.type=com.alibaba.cloud.nacos.registry.NacosServiceRegistry", - "spring.cloud.service-registry.auto-registration.enabled=true", - "spring.cloud.nacos.discovery.namespace=f7ad23e0-f581-4516-9420-8c50aa6a7b89", - "spring.cloud.nacos.discovery.metadata.key=value", - "eureka.client.service-url.defaultZone=http://127.0.0.1:8080/eureka", - } -) -@EnableAutoConfiguration -class MultipleServiceRegistryTest implements ApplicationListener { +import static org.junit.jupiter.api.Assertions.assertNull; - @Autowired - private ServiceRegistry serviceRegistry; +/** + * {@link MultipleServiceRegistry} Test + * + * @author Mercy + * @see MultipleServiceRegistry + * @since 1.0.0 + */ +class MultipleServiceRegistryTest { - @Autowired - private AutoServiceRegistrationProperties properties; + private DefaultRegistration defaultRegistration; - @Autowired - private Registration registration; + private MultipleRegistration registration; - @Autowired - private MultipleAutoServiceRegistration autoServiceRegistration; + private ServiceRegistry serviceRegistry; - @Override - public void onApplicationEvent(RegistrationPreRegisteredEvent event) { - this.registration.getMetadata().put("my-key", "my-value"); - if (event.getRegistration() instanceof EurekaRegistration) { - EurekaRegistration eurekaRegistration = (EurekaRegistration) event.getRegistration(); + private MultipleServiceRegistry multipleServiceRegistry; - - ApplicationInfoManager applicationInfoManager = eurekaRegistration.getApplicationInfoManager(); - InstanceInfo instanceInfo = applicationInfoManager.getInfo(); - Map metadata = registration.getMetadata(); - // Sync metadata from Registration to InstanceInfo - instanceInfo.getMetadata().putAll(metadata); - } + @BeforeEach + void setUp() { + this.defaultRegistration = createDefaultRegistration(); + this.registration = new MultipleRegistration(ofList(defaultRegistration)); + this.serviceRegistry = new InMemoryServiceRegistry(); + this.multipleServiceRegistry = new MultipleServiceRegistry(ofMap("default", serviceRegistry)); } @Test - void test() throws Exception { - assertNotNull(serviceRegistry); - assertNotNull(registration); - autoServiceRegistration.start(); - Thread.sleep(60 * 1000); - - autoServiceRegistration.stop(); + void testRegister() { + this.multipleServiceRegistry.register(this.registration); } @Test - void testMetaData() throws Exception { - assertNotNull(registration); - - autoServiceRegistration.start(); - - assertEquals(registration.getMetadata().get("my-key"), "my-value"); - Thread.sleep(60 * 1000); - - autoServiceRegistration.stop(); + void testDeregister() { + this.multipleServiceRegistry.deregister(this.registration); } + @Test + void testClose() { + this.multipleServiceRegistry.close(); + } + @Test + void testStatus() { + testRegister(); + this.multipleServiceRegistry.setStatus(this.registration, "UP"); + assertEquals("UP", this.multipleServiceRegistry.getStatus(this.registration)); + testDeregister(); + this.multipleServiceRegistry.setStatus(this.registration, "UP"); + assertNull(this.multipleServiceRegistry.getStatus(this.registration)); + } } \ No newline at end of file From 7f8ec973c8fd21075cb5ebfb8b211a65b925beff Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 15:31:07 +0800 Subject: [PATCH 075/310] Refactor getRegistrationClass and update related tests Simplified the getRegistrationClass method by removing AOP proxy checks and making it static. Updated MultipleServiceRegistry to use the refactored method and added a new test to verify its behavior with different ServiceRegistry implementations. --- .../service/registry/MultipleServiceRegistry.java | 11 +++-------- .../service/registry/MultipleServiceRegistryTest.java | 9 +++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java index ea794141..4ab21be5 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java @@ -11,7 +11,6 @@ import static io.microsphere.util.Assert.assertNotEmpty; import static org.springframework.aop.framework.AopProxyUtils.ultimateTargetClass; -import static org.springframework.aop.support.AopUtils.isAopProxy; import static org.springframework.core.ResolvableType.forClass; import static org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames; import static org.springframework.util.ClassUtils.resolveClassName; @@ -47,7 +46,7 @@ public MultipleServiceRegistry(Map registriesMap) { for (Map.Entry entry : registriesMap.entrySet()) { String beanName = entry.getKey(); ServiceRegistry serviceRegistry = entry.getValue(); - Class registrationClass = getRegistrationClass(serviceRegistry.getClass(), entry.getValue()); + Class registrationClass = getRegistrationClass(ultimateTargetClass(serviceRegistry)); beanNameToRegistrationTypesMap.put(beanName, registrationClass); defaultServiceRegistry = serviceRegistry; defaultRegistrationBeanName = beanName; @@ -95,7 +94,7 @@ public T getStatus(MultipleRegistration registration) { return (T) defaultServiceRegistry.getStatus(targetRegistration); } - private static Class getRegistrationClass(Class serviceRegistryClass, ServiceRegistry serviceRegistry) { + static Class getRegistrationClass(Class serviceRegistryClass) { Class registrationClass = forClass(serviceRegistryClass) .as(ServiceRegistry.class) .getGeneric(0) @@ -106,11 +105,7 @@ private static Class getRegistrationClass(Class registrationClassNames; - if (isAopProxy(serviceRegistry)) { - registrationClassNames = loadFactoryNames(ultimateTargetClass(serviceRegistry), classLoader); - } else { - registrationClassNames = loadFactoryNames(serviceRegistryClass, classLoader); - } + registrationClassNames = loadFactoryNames(serviceRegistryClass, classLoader); for (String registrationClassName : registrationClassNames) { registrationClass = resolveClassName(registrationClassName, classLoader); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java index 30002e45..a86f5697 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java @@ -18,6 +18,8 @@ package io.microsphere.spring.cloud.client.service.registry; +import com.alibaba.cloud.nacos.registry.NacosRegistration; +import com.alibaba.cloud.nacos.registry.NacosServiceRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; @@ -25,6 +27,7 @@ import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.Maps.ofMap; import static io.microsphere.spring.cloud.client.service.registry.MultipleRegistrationTest.createDefaultRegistration; +import static io.microsphere.spring.cloud.client.service.registry.MultipleServiceRegistry.getRegistrationClass; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -77,4 +80,10 @@ void testStatus() { this.multipleServiceRegistry.setStatus(this.registration, "UP"); assertNull(this.multipleServiceRegistry.getStatus(this.registration)); } + + @Test + void testGetRegistrationClass() { + assertEquals(MultipleRegistration.class, getRegistrationClass(this.multipleServiceRegistry.getClass())); + assertEquals(NacosRegistration.class, getRegistrationClass(NacosServiceRegistry.class)); + } } \ No newline at end of file From 5673c5f2ca1159180096f0f38ca22b80fe936334 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 15 Oct 2025 15:50:02 +0800 Subject: [PATCH 076/310] Update Testcontainers version to 2.0.0 Bumps the Testcontainers dependency version from 1.20.4 to 2.0.0 in the parent POM to use the latest features and improvements. --- microsphere-spring-cloud-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 8a22d821..1f33b24f 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -21,7 +21,7 @@ 0.2.2 - 1.20.4 + 2.0.0 From 26ca603c04728f2d537c71eb06bde9d642104021 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 09:11:14 +0800 Subject: [PATCH 077/310] Update microsphere-spring-boot BOM version to 0.2.3 Bumps the microsphere-spring-boot.version property in the parent POM from 0.2.2 to 0.2.3 to use the latest BOM release. --- microsphere-spring-cloud-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 1f33b24f..7d601750 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -20,7 +20,7 @@ - 0.2.2 + 0.2.3 2.0.0 From 93aa6a379fb1f130f8a3620af1b71f49c6210462 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 09:24:06 +0800 Subject: [PATCH 078/310] Add Zookeeper and Consul test dependencies Added spring-cloud-starter-zookeeper-discovery and spring-cloud-starter-consul-discovery as test-scoped dependencies to support testing with Zookeeper and Consul service discovery. --- microsphere-spring-cloud-commons/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index a9e9c7fc..00034aa0 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -125,5 +125,19 @@ test + + + org.springframework.cloud + spring-cloud-starter-zookeeper-discovery + test + + + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + test + + \ No newline at end of file From 5670ef2dc2b57d12c63befd08c291ada2727a61b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 10:58:27 +0800 Subject: [PATCH 079/310] Remove private modifier from current field Changed the 'current' field in WeightedRoundRobin from private to package-private to allow access from other classes within the package. --- .../cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java index b9596a00..1e65a771 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java @@ -15,7 +15,7 @@ public class WeightedRoundRobin { private volatile int weight; - private LongAdder current = new LongAdder(); + LongAdder current = new LongAdder(); private volatile long lastUpdate; From 2d91af8f44ca38e4a0704f67d76f9c996d7768ad Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 10:58:31 +0800 Subject: [PATCH 080/310] Add unit tests for WeightedRoundRobin class Introduces WeightedRoundRobinTest to verify the behavior of WeightedRoundRobin, including ID, weight, current value manipulation, selection logic, last update, and string representation. --- .../loadbalancer/WeightedRoundRobinTest.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobinTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobinTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobinTest.java new file mode 100644 index 00000000..1710392f --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobinTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.fault.tolerance.loadbalancer; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static io.microsphere.text.FormatUtils.format; +import static java.lang.System.currentTimeMillis; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link WeightedRoundRobin} Test + * + * @author Mercy + * @see WeightedRoundRobin + * @since 1.0.0 + */ +class WeightedRoundRobinTest { + + private WeightedRoundRobin weightedRoundRobin; + + @BeforeEach + void setUp() { + this.weightedRoundRobin = new WeightedRoundRobin("test-id"); + } + + @Test + void testGetId() { + assertEquals("test-id", weightedRoundRobin.getId()); + } + + @Test + void testWeight() { + this.weightedRoundRobin.setWeight(10); + assertEquals(10, this.weightedRoundRobin.getWeight()); + } + + @Test + void testIncreaseCurrent() { + this.weightedRoundRobin.setWeight(10); + assertEquals(10L, this.weightedRoundRobin.increaseCurrent()); + assertEquals(20L, this.weightedRoundRobin.increaseCurrent()); + assertEquals(30L, this.weightedRoundRobin.increaseCurrent()); + } + + @Test + void testSel() { + this.weightedRoundRobin.sel(10); + assertEquals(-10L, this.weightedRoundRobin.current.longValue()); + } + + @Test + void testLastUpdate() { + assertEquals(0L, this.weightedRoundRobin.getLastUpdate()); + long now = currentTimeMillis(); + this.weightedRoundRobin.setLastUpdate(now); + assertEquals(now, this.weightedRoundRobin.getLastUpdate()); + } + + @Test + void testToString() { + this.weightedRoundRobin.setWeight(10); + this.weightedRoundRobin.setLastUpdate(currentTimeMillis()); + assertEquals(format("WeightedRoundRobin[id='{}', weight={}, current={}, lastUpdate={}]", + this.weightedRoundRobin.getId(), + this.weightedRoundRobin.getWeight(), + this.weightedRoundRobin.current, + this.weightedRoundRobin.getLastUpdate()), + this.weightedRoundRobin.toString()); + } +} \ No newline at end of file From 36d0a9b1f4da0884f514970328dcb26b77a1182c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 11:10:19 +0800 Subject: [PATCH 081/310] Refactor LoadBalancerUtils constructor Removed the exception thrown in the private constructor of LoadBalancerUtils, making it an empty constructor. This change improves code clarity for this utility class. --- .../tolerance/loadbalancer/util/LoadBalancerUtils.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java index 52ed5717..d7a38353 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java @@ -8,10 +8,6 @@ */ public abstract class LoadBalancerUtils { - private LoadBalancerUtils() { - throw new UnsupportedOperationException(); - } - /** * Calculate the weight according to the uptime proportion of warmup time * the new weight will be within 1(inclusive) to weight(inclusive) @@ -25,4 +21,7 @@ public static int calculateWarmupWeight(long uptime, long warmup, int weight) { int ww = (int) (Math.round(Math.pow((uptime / (double) warmup), 2) * weight)); return ww < 1 ? 1 : (Math.min(ww, weight)); } + + private LoadBalancerUtils() { + } } From e3f6a662b522611e697eeb8e42944f1ea2309d50 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 11:10:23 +0800 Subject: [PATCH 082/310] Add unit test for LoadBalancerUtils warmup weight Introduces LoadBalancerUtilsTest to verify the calculateWarmupWeight method, ensuring correct weight calculation during warmup periods. --- .../util/LoadBalancerUtilsTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java new file mode 100644 index 00000000..eaf3b721 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.fault.tolerance.loadbalancer.util; + + +import org.junit.jupiter.api.Test; + +import static io.microsphere.spring.cloud.fault.tolerance.loadbalancer.util.LoadBalancerUtils.calculateWarmupWeight; +import static java.lang.System.currentTimeMillis; +import static java.lang.Thread.sleep; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link LoadBalancerUtils} Test + * + * @author Mercy + * @see LoadBalancerUtils + * @since 1.0.0 + */ +class LoadBalancerUtilsTest { + + @Test + void testCalculateWarmupWeight() throws InterruptedException { + long uptime = currentTimeMillis(); + int weight = 10; + for (int i = 0; i < 10; i++) { + assertTrue(weight <= (weight = calculateWarmupWeight(uptime, uptime, weight))); + sleep(10); + } + } +} \ No newline at end of file From 80f6a45e35612a5a7d12d25c3085a3e8b06be165 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 12:01:42 +0800 Subject: [PATCH 083/310] Add test for RegistrationMetaData constructor edge cases Introduced a new test to verify RegistrationMetaData handles null keys and values in registration metadata. Ensures that null values are correctly managed and do not cause unexpected behavior. --- .../service/registry/RegistrationMetaDataTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java index 6b4d8977..0c06892a 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java @@ -57,6 +57,16 @@ void setUp() { this.metaData = new RegistrationMetaData(ofList(defaultRegistration)); } + @Test + void testConstructor() { + Map metadata = this.defaultRegistration.getMetadata(); + metadata.put(null, "null"); + metadata.put("null", null); + this.metaData = new RegistrationMetaData(ofList(defaultRegistration)); + testGet(); + assertNull(this.metaData.get("null")); + } + @Test void testSize() { assertEquals(3, metaData.size()); From c6bcace21d7f44746ed9692c642a9c6b3fde4c61 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 13:04:51 +0800 Subject: [PATCH 084/310] Add test for getRegistrationClass with null return Introduced a new unit test to verify behavior when MultipleRegistration.special returns null during registration. This enhances test coverage for edge cases in MultipleServiceRegistry. --- .../registry/MultipleServiceRegistryTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java index a86f5697..5b4fcbf2 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java @@ -22,6 +22,7 @@ import com.alibaba.cloud.nacos.registry.NacosServiceRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import static io.microsphere.collection.Lists.ofList; @@ -86,4 +87,15 @@ void testGetRegistrationClass() { assertEquals(MultipleRegistration.class, getRegistrationClass(this.multipleServiceRegistry.getClass())); assertEquals(NacosRegistration.class, getRegistrationClass(NacosServiceRegistry.class)); } + + @Test + void testGetRegistrationClassWithNull() { + MultipleRegistration multipleRegistration = new MultipleRegistration(ofList(this.defaultRegistration)) { + @Override + public T special(Class specialClass) { + return null; + } + }; + this.multipleServiceRegistry.register(multipleRegistration); + } } \ No newline at end of file From 260346d7b2e1cb61b792c2fc3e89364864bf7c71 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 15:03:42 +0800 Subject: [PATCH 085/310] Remove unnecessary logger trace level check Simplified logging by removing the redundant isTraceEnabled() check before calling logger.trace in WebMvcServiceRegistryAutoConfiguration. --- .../WebMvcServiceRegistryAutoConfiguration.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index 7948e11b..b9ab1419 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -17,7 +17,6 @@ package io.microsphere.spring.cloud.client.service.registry.autoconfigure; import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; import io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent; import io.microsphere.spring.web.metadata.WebEndpointMapping; @@ -107,9 +106,7 @@ private void excludeMappings(Set mappings) { || isDispatcherServletMapping(mapping, patterns) || isActuatorWebEndpointMapping(patterns) ) { - if (logger.isTraceEnabled()) { - logger.trace("The '{}' was removed", mapping); - } + logger.trace("The '{}' was removed", mapping); iterator.remove(); } From 7d2db83e4e6c7f4f584b45f569f69abc9becc441 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 15:03:56 +0800 Subject: [PATCH 086/310] Refactor test to use static import for RANDOM_PORT Replaces explicit enum reference with a static import for SpringBootTest.WebEnvironment.RANDOM_PORT in WebMvcServiceRegistryAutoConfigurationTest for improved readability. --- .../WebMvcServiceRegistryAutoConfigurationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java index e0b49450..c6fc4bf7 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java @@ -27,6 +27,7 @@ import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_MAPPINGS_METADATA_NAME; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * {@link WebMvcServiceRegistryAutoConfiguration} Test @@ -44,7 +45,7 @@ "kubernetes.manifests.enabled=false", "kubernetes.reconciler.enabled=false" }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT + webEnvironment = RANDOM_PORT ) @EnableAutoConfiguration @EnableWebMvcExtension From 1dd347caa3bc92a682e031b3bb721a48f69ab54f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 15:04:02 +0800 Subject: [PATCH 087/310] Add unit tests for SimpleAutoServiceRegistration Introduces SimpleAutoServiceRegistrationTest to verify the behavior of SimpleAutoServiceRegistration, including configuration retrieval, enablement, and registration access methods. --- .../SimpleAutoServiceRegistrationTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java new file mode 100644 index 00000000..00fe5776 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; + +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link SimpleAutoServiceRegistration} Test + * + * @author Mercy + * @see SimpleAutoServiceRegistration + * @since 1.0.0 + */ +class SimpleAutoServiceRegistrationTest { + + private InMemoryServiceRegistry serviceRegistry; + + private AutoServiceRegistrationProperties properties; + + private DefaultRegistration registration; + + private SimpleAutoServiceRegistration autoServiceRegistration; + + @BeforeEach + void setUp() { + this.serviceRegistry = new InMemoryServiceRegistry(); + this.properties = new AutoServiceRegistrationProperties(); + this.registration = new DefaultRegistration(); + this.autoServiceRegistration = new SimpleAutoServiceRegistration(serviceRegistry, properties, registration); + } + + @Test + void testGetConfiguration() { + assertSame(this.properties, autoServiceRegistration.getConfiguration()); + } + + @Test + void testIsEnabled() { + assertSame(this.properties.isEnabled(), autoServiceRegistration.isEnabled()); + } + + @Test + void testGetRegistration() { + assertSame(this.registration, autoServiceRegistration.getRegistration()); + } + + @Test + void testGetManagementRegistration() { + assertSame(this.registration, autoServiceRegistration.getManagementRegistration()); + } +} \ No newline at end of file From 679e442cc409ba23ea019bf24993350240b83fb9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 15:16:07 +0800 Subject: [PATCH 088/310] Refactor dispatcher servlet mapping logic Replaces direct provider access with ifAvailable callback and uses ValueHolder to track mapping result. Also replaces Arrays.equals with arrayEquals for pattern comparison, improving code clarity and consistency. --- .../WebMvcServiceRegistryAutoConfiguration.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index b9ab1419..d838c25c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -20,6 +20,7 @@ import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; import io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent; import io.microsphere.spring.web.metadata.WebEndpointMapping; +import io.microsphere.util.ValueHolder; import jakarta.servlet.Filter; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -35,7 +36,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -45,6 +45,8 @@ import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; +import static io.microsphere.util.ArrayUtils.arrayEquals; +import static java.lang.Boolean.FALSE; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; /** @@ -129,16 +131,16 @@ private boolean isBuiltInFilterMapping(String[] patterns) { } private boolean isDispatcherServletMapping(WebEndpointMapping mapping, String[] patterns) { - DispatcherServletRegistrationBean registrationBean = dispatcherServletRegistrationBeanProvider.getIfAvailable(); - if (registrationBean != null) { + ValueHolder found = new ValueHolder<>(FALSE); + this.dispatcherServletRegistrationBeanProvider.ifAvailable(registrationBean -> { Object source = mapping.getEndpoint(); String servletName = registrationBean.getServletName(); if (Objects.equals(source, servletName)) { Collection urlMappings = registrationBean.getUrlMappings(); - return matchUrlPatterns(urlMappings, patterns); + found.setValue(matchUrlPatterns(urlMappings, patterns)); } - } - return false; + }); + return found.getValue(); } @@ -158,7 +160,7 @@ private boolean matchFilter(FilterRegistrationBean filterRegistrationBean, Strin private boolean matchUrlPatterns(Collection urlPatterns, String[] patterns) { String[] urlPatternsArray = urlPatterns.isEmpty() ? DEFAULT_URL_MAPPINGS : urlPatterns.toArray(EMPTY_STRING_ARRAY); - return Arrays.equals(urlPatternsArray, patterns); + return arrayEquals(urlPatternsArray, patterns); } } From 69166aea88a4453c917e5c4da1f2c731b528a5c7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 15:16:12 +0800 Subject: [PATCH 089/310] Simplify @SpringBootTest classes attribute usage Replaces array syntax with direct class reference for the 'classes' attribute in @SpringBootTest, improving code clarity in WebMvcServiceRegistryAutoConfigurationTest. --- .../WebMvcServiceRegistryAutoConfigurationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java index c6fc4bf7..f556848d 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java @@ -36,7 +36,7 @@ * @since 1.0.0 */ @SpringBootTest( - classes = {WebMvcServiceRegistryAutoConfigurationTest.class}, + classes = WebMvcServiceRegistryAutoConfigurationTest.class, properties = { "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", "spring.cloud.service-registry.auto-registration.enabled=true", From d4d37354c597beb299cafbaf13d0f3b636802f4d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 15:22:17 +0800 Subject: [PATCH 090/310] Update Testcontainers version to 2.0.1 Bumps the Testcontainers dependency version from 2.0.0 to 2.0.1 in the parent POM to include the latest fixes and improvements. --- microsphere-spring-cloud-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 7d601750..6b3626f0 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -21,7 +21,7 @@ 0.2.3 - 2.0.0 + 2.0.1 From 6b6bbb03d70e3bd0a226842b8da442b03be8b67b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 15:42:03 +0800 Subject: [PATCH 091/310] Reorder dependency management in parent POM Moved Testcontainers dependencies above Spring Boot and shifted Microsphere Spring Boot dependencies below Spring Cloud Alibaba for improved logical grouping and clarity in pom.xml. --- microsphere-spring-cloud-parent/pom.xml | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 6b3626f0..bf009d91 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -27,6 +27,15 @@ + + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + + org.springframework.boot @@ -45,15 +54,6 @@ import - - - io.github.microsphere-projects - microsphere-spring-boot-dependencies - ${microsphere-spring-boot.version} - pom - import - - com.alibaba.cloud @@ -63,11 +63,11 @@ import - + - org.testcontainers - testcontainers-bom - ${testcontainers.version} + io.github.microsphere-projects + microsphere-spring-boot-dependencies + ${microsphere-spring-boot.version} pom import From 4fa2ff1371349f47a74a0866e6ec5977dfa73412 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 23:53:49 +0800 Subject: [PATCH 092/310] Support multiple Registration types in MultipleRegistration Enhanced MultipleRegistration to map each registration to all assignable Registration types using ultimateTargetClass and findAllClasses. This allows for more flexible type-based lookups in the registration map. --- .../client/service/registry/MultipleRegistration.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java index 9b7ce348..dd3b9d55 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java @@ -5,9 +5,13 @@ import java.net.URI; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import static io.microsphere.util.Assert.assertNotEmpty; +import static io.microsphere.util.ClassUtils.findAllClasses; +import static io.microsphere.util.ClassUtils.isAssignableFrom; +import static org.springframework.aop.framework.AopProxyUtils.ultimateTargetClass; /** * The Delegating {@link Registration} for the multiple service registration @@ -29,8 +33,9 @@ public MultipleRegistration(Collection registrations) { assertNotEmpty(registrations, () -> "registrations cannot be empty"); //init map for (Registration registration : registrations) { - Class clazz = registration.getClass(); - this.registrationMap.put(clazz, registration); + Class clazz = (Class) ultimateTargetClass(registration); + List> classes = (List) findAllClasses(clazz, type -> isAssignableFrom(Registration.class, type) && !Registration.class.equals(type)); + classes.forEach(type -> registrationMap.put(type, registration)); this.defaultRegistration = registration; } this.metaData = new RegistrationMetaData(registrations); From 82f2c39c6c01146ff5dbd201fb9412656c87855c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 23:54:11 +0800 Subject: [PATCH 093/310] Initialize ServiceInstance for Zookeeper registrations Added logic to initialize ServiceInstance for Registration objects of type ServiceInstanceRegistration. This ensures that Zookeeper-specific service instances are properly initialized during RegistrationMetaData construction. --- .../client/service/registry/RegistrationMetaData.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java index 0b9c2a85..1790fe38 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java @@ -1,6 +1,7 @@ package io.microsphere.spring.cloud.client.service.registry; import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration; import org.springframework.util.CollectionUtils; import java.util.Collection; @@ -21,7 +22,9 @@ public final class RegistrationMetaData implements Map { * MetaData information manually added by the application,usually specified by configuration */ private final Map applicationMetaData; + private final Collection registrations; + private final Object lock = new Object(); public RegistrationMetaData(Collection registrations) { @@ -29,6 +32,12 @@ public RegistrationMetaData(Collection registrations) { this.registrations = registrations; this.applicationMetaData = new ConcurrentHashMap<>(); for (Registration registration : registrations) { + if (registration instanceof ServiceInstanceRegistration) { + ServiceInstanceRegistration serviceInstanceRegistration = (ServiceInstanceRegistration) registration; + // init ServiceInstance + serviceInstanceRegistration.getServiceInstance(); + } + Map metaData = registration.getMetadata(); if (!CollectionUtils.isEmpty(metaData)) { //check key and value must not be null From 3d9aca84cc774fed8933c3e3484ccd0758091e4d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 20 Oct 2025 23:54:15 +0800 Subject: [PATCH 094/310] Enable Testcontainers integration for service registry tests Added Testcontainers setup to CI workflow and introduced a Docker Compose file for service registry servers. Updated integration tests to use Testcontainers and enabled them conditionally. Moved service registry dependencies from test to optional scope and added a Maven profile for Testcontainers support. --- .github/workflows/maven-build.yml | 7 +- microsphere-spring-cloud-commons/pom.xml | 69 ++++++------- ...ultipleServiceRegistryIntegrationTest.java | 96 ++++++++++--------- .../docker/service-registry-servers.yml | 25 +++++ microsphere-spring-cloud-parent/pom.xml | 17 ++++ 5 files changed, 136 insertions(+), 78 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/resources/META-INF/docker/service-registry-servers.yml diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index f9c4cfcf..27eeb427 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -25,6 +25,11 @@ jobs: - name: Checkout Source uses: actions/checkout@v4 + - name: Setup Testcontainers Cloud Client + uses: atomicjar/testcontainers-cloud-setup-action@v1 + with: + token: ${{ secrets.TC_CLOUD_TOKEN }} + - name: Setup JDK ${{ matrix.Java }} uses: actions/setup-java@v4 with: @@ -39,7 +44,7 @@ jobs: --file pom.xml -Drevision=0.0.1-SNAPSHOT test - --activate-profiles test,coverage,${{ matrix.maven-profile-spring-cloud }} + --activate-profiles test,coverage,testcontainers,${{ matrix.maven-profile-spring-cloud }} - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v5 diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index 00034aa0..606e0c45 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -67,6 +67,41 @@ true + + + + org.springframework.cloud + spring-cloud-netflix-eureka-client + true + + + + com.netflix.eureka + eureka-client + true + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + true + + + + + org.springframework.cloud + spring-cloud-starter-zookeeper-discovery + true + + + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + true + + io.github.microsphere-projects @@ -105,39 +140,5 @@ - - - org.springframework.cloud - spring-cloud-netflix-eureka-client - test - - - - com.netflix.eureka - eureka-client - test - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - test - - - - - org.springframework.cloud - spring-cloud-starter-zookeeper-discovery - test - - - - - org.springframework.cloud - spring-cloud-starter-consul-discovery - test - - \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java index ca33c082..de496c9d 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java @@ -1,35 +1,36 @@ package io.microsphere.spring.cloud.client.service.registry; -import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration; -import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration; -import com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.InstanceInfo; -import io.microsphere.spring.cloud.client.service.registry.autoconfigure.ServiceRegistryAutoConfiguration; import io.microsphere.spring.cloud.client.service.registry.event.RegistrationPreRegisteredEvent; -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.client.CommonsClientAutoConfiguration; -import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.cloud.commons.util.UtilAutoConfiguration; -import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; -import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration; import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.containers.ComposeContainer; +import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; +import java.io.File; +import java.net.URL; import java.util.Map; +import static io.microsphere.lang.function.ThrowableAction.execute; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.testcontainers.containers.wait.strategy.Wait.forLogMessage; /** * {@link MultipleServiceRegistry} Integration Test @@ -39,34 +40,29 @@ * @see MultipleServiceRegistry * @since 1.0.0 */ -@Disabled +@EnabledIfSystemProperty(named = "testcontainers.enabled", matches = "true") +@EnabledIfDockerAvailable @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { - AutoServiceRegistrationAutoConfiguration.class, - CommonsClientAutoConfiguration.class, - EurekaClientAutoConfiguration.class, - DiscoveryClientOptionalArgsConfiguration.class, - NacosServiceRegistryAutoConfiguration.class, - NacosServiceAutoConfiguration.class, - NacosDiscoveryAutoConfiguration.class, - UtilAutoConfiguration.class, MultipleServiceRegistryIntegrationTest.class, - ServiceRegistryAutoConfiguration.class, }) @TestPropertySource( properties = { "spring.application.name=test", - "microsphere.spring.cloud.multiple-registration.enabled=true", - "microsphere.spring.cloud.default-registration.type=com.alibaba.cloud.nacos.registry.NacosRegistration", - "microsphere.spring.cloud.default-service-registry.type=com.alibaba.cloud.nacos.registry.NacosServiceRegistry", + "spring.cloud.service-registry.auto-registration.enabled=true", "spring.cloud.nacos.discovery.namespace=f7ad23e0-f581-4516-9420-8c50aa6a7b89", "spring.cloud.nacos.discovery.metadata.key=value", - "eureka.client.service-url.defaultZone=http://127.0.0.1:8080/eureka", + + "microsphere.spring.cloud.multiple-registration.enabled=true", + "microsphere.spring.cloud.default-registration.type=com.alibaba.cloud.nacos.registry.NacosRegistration", + "microsphere.spring.cloud.default-service-registry.type=com.alibaba.cloud.nacos.registry.NacosServiceRegistry", } ) @EnableAutoConfiguration -class MultipleServiceRegistryIntegrationTest implements ApplicationListener { +public class MultipleServiceRegistryIntegrationTest implements ApplicationListener { + + private static ComposeContainer composeContainer; @Autowired private ServiceRegistry serviceRegistry; @@ -80,11 +76,39 @@ class MultipleServiceRegistryIntegrationTest implements ApplicationListener metadata = registration.getMetadata(); @@ -95,25 +119,11 @@ public void onApplicationEvent(RegistrationPreRegisteredEvent event) { @Test void test() throws Exception { - assertNotNull(serviceRegistry); - assertNotNull(registration); - autoServiceRegistration.start(); - Thread.sleep(60 * 1000); - - autoServiceRegistration.stop(); - } - - @Test - void testMetaData() throws Exception { assertNotNull(registration); + execute(autoServiceRegistration::start, e -> { - autoServiceRegistration.start(); - + }); assertEquals(registration.getMetadata().get("my-key"), "my-value"); - Thread.sleep(60 * 1000); - autoServiceRegistration.stop(); } - - } \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/resources/META-INF/docker/service-registry-servers.yml b/microsphere-spring-cloud-commons/src/test/resources/META-INF/docker/service-registry-servers.yml new file mode 100644 index 00000000..8f34de59 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/resources/META-INF/docker/service-registry-servers.yml @@ -0,0 +1,25 @@ +services: + zookeeper: + image: zookeeper + restart: always + ports: + - "2181:2181" + + consul: + image: hashicorp/consul + ports: + - "8500:8500" + + nacos: + image: nacos/nacos-server:v2.5.1 + ports: + - "8848:8848" + environment: + - MODE=standalone + + eureka: + image: aliaksandrvoitko/eureka-server + ports: + - "8761:8761" + environment: + - SERVER_PORT=8761 \ No newline at end of file diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index bf009d91..d858de55 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -212,5 +212,22 @@ + + testcontainers + + + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + + + + + \ No newline at end of file From 63d26520844d46f6f2d88341ee7f8e5f25c73515 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 00:10:14 +0800 Subject: [PATCH 095/310] Refactor Zookeeper registration initialization logic Replaces direct reference to ServiceInstanceRegistration with reflection-based initialization using class name and method name constants. This improves decoupling from Zookeeper-specific classes and avoids compile-time dependencies. --- .../registry/RegistrationMetaData.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java index 1790fe38..e32da6b4 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java @@ -1,7 +1,6 @@ package io.microsphere.spring.cloud.client.service.registry; import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration; import org.springframework.util.CollectionUtils; import java.util.Collection; @@ -10,7 +9,9 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import static io.microsphere.reflect.MethodUtils.invokeMethod; import static io.microsphere.util.Assert.assertNotEmpty; +import static org.springframework.aop.framework.AopProxyUtils.ultimateTargetClass; /** * @author 韩超 @@ -18,6 +19,16 @@ */ public final class RegistrationMetaData implements Map { + /** + * The class name of {@link org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration} + */ + static final String ZOOKEEPER_REGISTRATION_CLASS_NAME = "org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration"; + + /** + * The method name of {@link org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration#getServiceInstance()} + */ + static final String GET_SERVICE_INSTANCE_METHOD_NAME = "getServiceInstance"; + /** * MetaData information manually added by the application,usually specified by configuration */ @@ -32,11 +43,7 @@ public RegistrationMetaData(Collection registrations) { this.registrations = registrations; this.applicationMetaData = new ConcurrentHashMap<>(); for (Registration registration : registrations) { - if (registration instanceof ServiceInstanceRegistration) { - ServiceInstanceRegistration serviceInstanceRegistration = (ServiceInstanceRegistration) registration; - // init ServiceInstance - serviceInstanceRegistration.getServiceInstance(); - } + initializeIfZookeeperRegistrationAvailable(registration); Map metaData = registration.getMetadata(); if (!CollectionUtils.isEmpty(metaData)) { @@ -127,4 +134,12 @@ public Collection values() { public Set> entrySet() { return this.applicationMetaData.entrySet(); } + + private void initializeIfZookeeperRegistrationAvailable(Registration registration) { + Class registrationClass = ultimateTargetClass(registration); + if (ZOOKEEPER_REGISTRATION_CLASS_NAME.equals(registrationClass.getName())) { + // init ServiceInstance + invokeMethod(registration, GET_SERVICE_INSTANCE_METHOD_NAME); + } + } } From 986fab9e12f9d56e5d57112a2cb5e47fdf6bf75e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 00:10:18 +0800 Subject: [PATCH 096/310] Update test dependencies in pom.xml Reordered and updated test dependencies: replaced 'junit-jupiter' with 'testcontainers-junit-jupiter' for Testcontainers, and moved 'spring-boot-starter-test' to the end of the test dependencies list for better organization. --- microsphere-spring-cloud-commons/pom.xml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index 606e0c45..9c76ad01 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -121,16 +121,11 @@ - - org.springframework.boot - spring-boot-starter-test - test - org.testcontainers - junit-jupiter + testcontainers-junit-jupiter test @@ -140,5 +135,12 @@ + + org.springframework.boot + spring-boot-starter-test + test + + + \ No newline at end of file From 63e405f0bf2825129b00f3debec6d35c67d764d2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 00:14:02 +0800 Subject: [PATCH 097/310] Reorder spring-boot-starter-test dependency in pom.xml Moved the spring-boot-starter-test dependency higher in the dependencies list for improved organization and clarity in the pom.xml file. --- microsphere-spring-cloud-commons/pom.xml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index 9c76ad01..287b0ba6 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -121,6 +121,11 @@ + + org.springframework.boot + spring-boot-starter-test + test + @@ -135,12 +140,5 @@ - - org.springframework.boot - spring-boot-starter-test - test - - - \ No newline at end of file From 3a0dce55fa902337f73bd2e20d993ef7bb3b9114 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 09:22:04 +0800 Subject: [PATCH 098/310] Remove Spring Cloud service discovery profiles from pom.xml Deleted profiles for Eureka, Nacos, Zookeeper, Consul, and Kubernetes from the parent POM. This streamlines the configuration and removes test dependencies for these service discovery solutions. --- microsphere-spring-cloud-parent/pom.xml | 98 +------------------------ 1 file changed, 2 insertions(+), 96 deletions(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index d858de55..3ad34b18 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -77,6 +77,7 @@ + spring-cloud-2022 @@ -116,102 +117,7 @@ - - - spring-cloud-netflix-eureka - - - org.springframework.cloud - spring-cloud-netflix-eureka-client - test - - - - com.netflix.eureka - eureka-client - test - - - - org.springframework.cloud - spring-cloud-netflix-eureka-server - test - - - - - - - spring-cloud-alibaba-nacos - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - test - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-config - test - - - - - - - spring-cloud-zookeeper - - - org.springframework.cloud - spring-cloud-starter-zookeeper-discovery - test - - - - org.springframework.cloud - spring-cloud-starter-zookeeper-config - test - - - - - - - spring-cloud-consul - - - org.springframework.cloud - spring-cloud-starter-consul-discovery - test - - - - org.springframework.cloud - spring-cloud-starter-consul-config - test - - - - org.testcontainers - consul - test - - - - - - - spring-cloud-kubernetes - - - org.springframework.cloud - spring-cloud-starter-kubernetes-client-all - test - - - - + testcontainers From f106ebe4b0572917734b65b261428a154df114bd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 12:57:20 +0800 Subject: [PATCH 099/310] Remove test application.yaml configuration file Deleted the application.yaml file from test resources in microsphere-spring-cloud-commons. This file contained various Spring Cloud service discovery and configuration profiles for testing purposes. --- .../src/test/resources/application.yaml | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 microsphere-spring-cloud-commons/src/test/resources/application.yaml diff --git a/microsphere-spring-cloud-commons/src/test/resources/application.yaml b/microsphere-spring-cloud-commons/src/test/resources/application.yaml deleted file mode 100644 index 943d64ad..00000000 --- a/microsphere-spring-cloud-commons/src/test/resources/application.yaml +++ /dev/null @@ -1,92 +0,0 @@ -spring: - application: - name: microsphere-spring-cloud-commons-application - - # default disable all - cloud: - nacos: - discovery: - enabled: false - register-enabled: false - config: - import-check.enabled: false - zookeeper: - enabled: false - consul: - enabled: false - kubernetes: - enabled: false - config: - enabled: false - enableApi: false - secrets: - enabled: false - -eureka: - client: - enabled: false - - ---- -spring: - config: - activate: - on-profile: nacos - - cloud: - nacos: - username: nacos - password: nacos - discovery: - enabled: true - register-enabled: true - server-addr: 127.0.0.1:8848 - ephemeral: false - - ---- -spring: - config: - activate: - on-profile: eureka - -eureka: - client: - enabled: true - service-url: - defaultZone: http://127.0.0.1:8761/eureka/ - - ---- -spring: - config: - activate: - on-profile: zookeeper - cloud: - zookeeper: - enabled: true - connect-string: 127.0.0.1:2181 - - ---- -spring: - config: - activate: - on-profile: consul - - cloud: - consul: - enabled: true - host: 127.0.0.1 - port: 8500 - - ---- -spring: - config: - activate: - on-profile: kubernetes - - cloud: - kubernetes: - enabled: true \ No newline at end of file From 2089d1b31bf56d4cad6a203b7fb5ab1a5be0b093 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 15:05:03 +0800 Subject: [PATCH 100/310] Add test configuration for multiple cloud profiles Introduces a test.yaml file with default and profile-specific settings for Nacos, Eureka, Zookeeper, Consul, and Kubernetes. This enables testing with different Spring Cloud service discovery and configuration backends by activating corresponding profiles. --- .../META-INF/config/default/test.yaml | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/resources/META-INF/config/default/test.yaml diff --git a/microsphere-spring-cloud-commons/src/test/resources/META-INF/config/default/test.yaml b/microsphere-spring-cloud-commons/src/test/resources/META-INF/config/default/test.yaml new file mode 100644 index 00000000..943d64ad --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/resources/META-INF/config/default/test.yaml @@ -0,0 +1,92 @@ +spring: + application: + name: microsphere-spring-cloud-commons-application + + # default disable all + cloud: + nacos: + discovery: + enabled: false + register-enabled: false + config: + import-check.enabled: false + zookeeper: + enabled: false + consul: + enabled: false + kubernetes: + enabled: false + config: + enabled: false + enableApi: false + secrets: + enabled: false + +eureka: + client: + enabled: false + + +--- +spring: + config: + activate: + on-profile: nacos + + cloud: + nacos: + username: nacos + password: nacos + discovery: + enabled: true + register-enabled: true + server-addr: 127.0.0.1:8848 + ephemeral: false + + +--- +spring: + config: + activate: + on-profile: eureka + +eureka: + client: + enabled: true + service-url: + defaultZone: http://127.0.0.1:8761/eureka/ + + +--- +spring: + config: + activate: + on-profile: zookeeper + cloud: + zookeeper: + enabled: true + connect-string: 127.0.0.1:2181 + + +--- +spring: + config: + activate: + on-profile: consul + + cloud: + consul: + enabled: true + host: 127.0.0.1 + port: 8500 + + +--- +spring: + config: + activate: + on-profile: kubernetes + + cloud: + kubernetes: + enabled: true \ No newline at end of file From 532c002380a0ce1b3dfbd2f907dc922cbbc1ecb2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 15:34:01 +0800 Subject: [PATCH 101/310] Add integration test for UnionDiscoveryClient Introduces UnionDiscoveryClientIntegrationTest to verify the integration of UnionDiscoveryClient with multiple service registries using Testcontainers and Docker Compose. The test ensures correct composition and service discovery behavior. --- .../UnionDiscoveryClientIntegrationTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientIntegrationTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientIntegrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientIntegrationTest.java new file mode 100644 index 00000000..46eaee33 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientIntegrationTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.microsphere.spring.cloud.client.discovery.autoconfigure; + +import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.containers.ComposeContainer; +import org.testcontainers.junit.jupiter.EnabledIfDockerAvailable; + +import java.io.File; +import java.net.URL; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.testcontainers.containers.wait.strategy.Wait.forLogMessage; + +/** + * {@link UnionDiscoveryClient} Integration Test + * + * @author Mercy + * @since 1.0.0 + */ +@EnabledIfSystemProperty(named = "testcontainers.enabled", matches = "true") +@EnabledIfDockerAvailable +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + UnionDiscoveryClientIntegrationTest.class +}) +@TestPropertySource( + properties = { + "spring.application.name=test", + + "spring.cloud.service-registry.auto-registration.enabled=true", + + "microsphere.spring.cloud.client.discovery.mode=union", + + "microsphere.spring.cloud.multiple-registration.enabled=true" + } +) +@EnableAutoConfiguration +public class UnionDiscoveryClientIntegrationTest { + + private static ComposeContainer composeContainer; + + @Autowired + private DiscoveryClient discoveryClient; + + @BeforeAll + static void beforeAll() throws Exception { + ClassLoader classLoader = UnionDiscoveryClientIntegrationTest.class.getClassLoader(); + URL resource = classLoader.getResource("META-INF/docker/service-registry-servers.yml"); + File dockerComposeFile = new File(resource.toURI()); + composeContainer = new ComposeContainer(dockerComposeFile); + composeContainer.waitingFor("nacos", forLogMessage(".*Nacos started successfully.*", 1)) + .waitingFor("eureka", forLogMessage(".*Started EurekaServerApplication.*", 1)) + .start(); + } + + @AfterAll + static void afterAll() { + composeContainer.stop(); + } + + @Test + void test() { + assertEquals(CompositeDiscoveryClient.class, discoveryClient.getClass()); + CompositeDiscoveryClient compositeDiscoveryClient = CompositeDiscoveryClient.class.cast(discoveryClient); + List discoveryClients = compositeDiscoveryClient.getDiscoveryClients(); + assertEquals(6, discoveryClients.size()); + assertEquals(UnionDiscoveryClient.class, discoveryClients.get(0).getClass()); + List services = compositeDiscoveryClient.getServices(); + assertTrue(services.size() > 1); + } +} From ac0389af8530dc330be7964db3dc0e4862cffc4a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 17:28:46 +0800 Subject: [PATCH 102/310] Refactor test package structure and class modifiers Moved UnionDiscoveryClientIntegrationTest and UnionDiscoveryClientTest from 'discovery.autoconfigure' to 'discovery' package. Changed test classes from public to package-private. Removed unnecessary imports and updated test configuration references for improved clarity and organization. --- .../UnionDiscoveryClientIntegrationTest.java | 22 +++---- .../UnionDiscoveryClientTest.java | 2 +- .../DiscoveryClientAutoConfigurationTest.java | 4 +- .../ServiceRegistrationEndpointTest.java | 66 +++++++++++++++++++ 4 files changed, 76 insertions(+), 18 deletions(-) rename microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/{autoconfigure => }/UnionDiscoveryClientIntegrationTest.java (87%) rename microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/{autoconfigure => }/UnionDiscoveryClientTest.java (98%) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientIntegrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientIntegrationTest.java similarity index 87% rename from microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientIntegrationTest.java rename to microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientIntegrationTest.java index 46eaee33..bba97aea 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientIntegrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientIntegrationTest.java @@ -14,9 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.microsphere.spring.cloud.client.discovery.autoconfigure; +package io.microsphere.spring.cloud.client.discovery; -import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -52,19 +51,14 @@ @ContextConfiguration(classes = { UnionDiscoveryClientIntegrationTest.class }) -@TestPropertySource( - properties = { - "spring.application.name=test", - - "spring.cloud.service-registry.auto-registration.enabled=true", - - "microsphere.spring.cloud.client.discovery.mode=union", - - "microsphere.spring.cloud.multiple-registration.enabled=true" - } -) +@TestPropertySource(properties = { + "spring.application.name=test", + "spring.cloud.service-registry.auto-registration.enabled=true", + "microsphere.spring.cloud.client.discovery.mode=union", + "microsphere.spring.cloud.multiple-registration.enabled=true" +}) @EnableAutoConfiguration -public class UnionDiscoveryClientIntegrationTest { +class UnionDiscoveryClientIntegrationTest { private static ComposeContainer composeContainer; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientTest.java similarity index 98% rename from microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java rename to microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientTest.java index ff7807ab..68b8bca2 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/UnionDiscoveryClientTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientTest.java @@ -57,7 +57,7 @@ "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1" } ) -public class UnionDiscoveryClientTest { +class UnionDiscoveryClientTest { @Autowired private DiscoveryClient discoveryClient; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java index e5092dd4..8d5746e2 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java @@ -17,7 +17,6 @@ package io.microsphere.spring.cloud.client.discovery.autoconfigure; import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; -import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClientTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -48,8 +47,7 @@ UtilAutoConfiguration.class, SimpleDiscoveryClientAutoConfiguration.class, CompositeDiscoveryClientAutoConfiguration.class, - DiscoveryClientAutoConfiguration.class, - UnionDiscoveryClientTest.class + DiscoveryClientAutoConfiguration.class }) @TestPropertySource( properties = { diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java new file mode 100644 index 00000000..6edb3170 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.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 io.microsphere.spring.cloud.client.service.registry.endpoint; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration; +import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * {@link ServiceRegistrationEndpoint} Test + * + * @author Mercy + * @see ServiceRegistrationEndpoint + * @since 1.0.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + UtilAutoConfiguration.class, + SimpleDiscoveryClientAutoConfiguration.class, + CompositeDiscoveryClientAutoConfiguration.class +}) +@TestPropertySource( + properties = { + "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", + "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", + "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", + "spring.cloud.discovery.client.simple.instances.test[0].port=8080", + "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1" + } +) +class ServiceRegistrationEndpointTest { + + @BeforeEach + void setUp() { + } + + @Test + void testMetadata() { + } + + @Test + void testStart() { + } +} \ No newline at end of file From 8f3e3bcf3c30b2f22dc495256c6844ab4064fe39 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 17:29:03 +0800 Subject: [PATCH 103/310] Update MultipleServiceRegistryIntegrationTest.java --- .../registry/MultipleServiceRegistryIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java index de496c9d..3c00c890 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryIntegrationTest.java @@ -60,7 +60,7 @@ } ) @EnableAutoConfiguration -public class MultipleServiceRegistryIntegrationTest implements ApplicationListener { +class MultipleServiceRegistryIntegrationTest implements ApplicationListener { private static ComposeContainer composeContainer; From a136d94eb48a89896768eaef7f5948a0972615aa Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 18:58:45 +0800 Subject: [PATCH 104/310] Refactor property constants in auto-registration config Updated the definition and usage of ENABLED_PROPERTY_NAME to use @ConfigurationProperty annotation and PropertyConstants. Adjusted imports and @ConditionalOnProperty usage for improved clarity and consistency. --- ...utoServiceRegistrationAutoConfiguration.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java index 81b46618..0182b574 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java @@ -16,6 +16,8 @@ */ package io.microsphere.spring.cloud.client.service.registry.autoconfigure; +import io.microsphere.annotation.ConfigurationProperty; +import io.microsphere.constants.PropertyConstants; import io.microsphere.spring.cloud.client.service.registry.DefaultRegistration; import io.microsphere.spring.cloud.client.service.registry.InMemoryServiceRegistry; import io.microsphere.spring.cloud.client.service.registry.SimpleAutoServiceRegistration; @@ -36,8 +38,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import static io.microsphere.constants.PropertyConstants.ENABLED_PROPERTY_NAME; -import static io.microsphere.spring.cloud.client.service.registry.autoconfigure.SimpleAutoServiceRegistrationAutoConfiguration.PROPERTY_NAME_PREFIX; +import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; +import static io.microsphere.spring.cloud.client.service.registry.autoconfigure.SimpleAutoServiceRegistrationAutoConfiguration.ENABLED_PROPERTY_NAME; import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX; /** @@ -48,7 +50,7 @@ * @since 1.0.0 */ @Configuration(proxyBeanMethods = false) -@ConditionalOnProperty(prefix = PROPERTY_NAME_PREFIX, name = ENABLED_PROPERTY_NAME) +@ConditionalOnProperty(name = ENABLED_PROPERTY_NAME) @ConditionalOnAutoServiceRegistrationEnabled @AutoConfigureBefore(value = { AutoServiceRegistrationAutoConfiguration.class @@ -67,6 +69,15 @@ public class SimpleAutoServiceRegistrationAutoConfiguration { */ public static final String PROPERTY_NAME_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "service-registry.auto-registration.simple."; + /** + * The property name : "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled" + */ + @ConfigurationProperty( + type = boolean.class, + source = APPLICATION_SOURCE + ) + public static final String ENABLED_PROPERTY_NAME = PROPERTY_NAME_PREFIX + PropertyConstants.ENABLED_PROPERTY_NAME; + @Bean public Registration registration( @Value("${spring.application.name:default}") String applicationName, From 851570ea551b70f71f4d7fe2c3848f831396a7a2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 18:59:01 +0800 Subject: [PATCH 105/310] Add logger to AbstractServiceRegistrationEndpoint Introduced a protected Logger instance to AbstractServiceRegistrationEndpoint using microsphere logging. This enables logging capabilities for subclasses and improves observability. --- .../endpoint/AbstractServiceRegistrationEndpoint.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java index a0869aa0..8562f06e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java @@ -1,5 +1,6 @@ package io.microsphere.spring.cloud.client.service.registry.endpoint; +import io.microsphere.logging.Logger; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; @@ -11,6 +12,8 @@ import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import org.springframework.context.ApplicationListener; +import static io.microsphere.logging.LoggerFactory.getLogger; + /** * Abstract Endpoint for Service Registration * @@ -19,6 +22,8 @@ */ public class AbstractServiceRegistrationEndpoint implements SmartInitializingSingleton, ApplicationListener { + protected final Logger logger = getLogger(getClass()); + @Value("${spring.application.name}") protected String applicationName; From 03be99b08c0636d512742dd29ba9918de854a3b0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 18:59:06 +0800 Subject: [PATCH 106/310] Simplify logger usage in ServiceDeregistrationEndpoint Removed conditional checks for logger levels and directly log info and warning messages during service deregistration. This streamlines the logging logic in the endpoint. --- .../endpoint/ServiceDeregistrationEndpoint.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java index ea74aaa4..732e5ea2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java @@ -1,13 +1,9 @@ package io.microsphere.spring.cloud.client.service.registry.endpoint; -import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration; -import static io.microsphere.logging.LoggerFactory.getLogger; - /** * The {@link Endpoint @Endpoint} for Service Deregistration * @@ -19,21 +15,15 @@ @Endpoint(id = "serviceDeregistration") public class ServiceDeregistrationEndpoint extends AbstractServiceRegistrationEndpoint { - private final Logger logger = getLogger(getClass()); - @WriteOperation public boolean stop() { boolean isRunning = isRunning(); if (isRunning) { serviceRegistry.deregister(registration); - if(logger.isInfoEnabled()) { - logger.info("Service[name : '{}'] is deregistered!", applicationName); - } + logger.info("Service[name : '{}'] is deregistered!", applicationName); setRunning(false); } else { - if (logger.isWarnEnabled()) { - logger.warn("Service[name : '{}'] is not registered, deregistration can't be executed!", applicationName); - } + logger.warn("Service[name : '{}'] is not registered, deregistration can't be executed!", applicationName); } return isRunning; } From 80ae9f2e6c5f51d663c3730b2a3cdbbeb1a4fb55 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 18:59:10 +0800 Subject: [PATCH 107/310] Remove unused logger and simplify logging Eliminated the unused Logger instance and related imports. Logging statements now use the logger directly without level checks, streamlining the code in ServiceRegistrationEndpoint. --- .../endpoint/ServiceRegistrationEndpoint.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java index 05924ce2..500fe506 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java @@ -1,7 +1,5 @@ package io.microsphere.spring.cloud.client.service.registry.endpoint; -import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; @@ -12,8 +10,6 @@ import java.util.LinkedHashMap; import java.util.Map; -import static io.microsphere.logging.LoggerFactory.getLogger; - /** * The {@link Endpoint @Endpoint} for Service Registration * @@ -25,13 +21,9 @@ @Endpoint(id = "serviceRegistration") public class ServiceRegistrationEndpoint extends AbstractServiceRegistrationEndpoint { - private final Logger logger = getLogger(getClass()); - @ReadOperation public Map metadata() { - - Map metadata = new LinkedHashMap<>(); - + Map metadata = new LinkedHashMap<>(16); metadata.put("application-name", applicationName); metadata.put("registration", registration); metadata.put("port", port); @@ -57,13 +49,9 @@ public boolean start() { if (!isRunning) { serviceRegistry.register(registration); setRunning(true); - if(logger.isTraceEnabled()) { - logger.trace("Service[name : '{}'] is registered!", applicationName); - } + logger.info("Service[name : '{}'] is registered!", applicationName); } else { - if(logger.isWarnEnabled()) { - logger.warn("Service[name : '{}'] was registered!", applicationName); - } + logger.warn("Service[name : '{}'] was registered!", applicationName); } return isRunning; } From de38363c5fcb30635cef6d909582b9b6b0cf3e7b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 18:59:16 +0800 Subject: [PATCH 108/310] Add base test for AbstractServiceRegistrationEndpoint Introduces AbstractServiceRegistrationEndpointTest to provide a foundation for testing service registration endpoints using Spring Boot and JUnit. Sets up application context with relevant properties and autowires required beans for further test development. --- ...stractServiceRegistrationEndpointTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java new file mode 100644 index 00000000..b78e2cde --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry.endpoint; + + +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * {@link AbstractServiceRegistrationEndpoint} Base Test + * + * @author Mercy + * @see AbstractServiceRegistrationEndpoint + * @since 1.0.0 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest( + properties = { + "spring.application.name=test-app", + "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", + "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", + "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", + "spring.cloud.discovery.client.simple.instances.test[0].port=8080", + "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1", + "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true" + }, + webEnvironment = RANDOM_PORT +) +@EnableAutoConfiguration +class AbstractServiceRegistrationEndpointTest { + + @Autowired + protected Registration registration; + + @LocalServerPort + protected Integer port; + + @Autowired + protected AutoServiceRegistrationProperties autoServiceRegistrationProperties; +} \ No newline at end of file From bfc5f5160972fe100c43a484945f79898f7bdbab Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 18:59:23 +0800 Subject: [PATCH 109/310] Refactor ServiceRegistrationEndpointTest structure Updated ServiceRegistrationEndpointTest to extend AbstractServiceRegistrationEndpointTest, removed unused imports and configuration annotations, and improved test assertions for metadata and start methods. This refactor streamlines the test setup and enhances clarity. --- .../ServiceRegistrationEndpointTest.java | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java index 6edb3170..2f8cd269 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java @@ -18,15 +18,19 @@ package io.microsphere.spring.cloud.client.service.registry.endpoint; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration; -import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration; -import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Map; + +import static java.lang.Boolean.TRUE; +import static java.lang.Integer.valueOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link ServiceRegistrationEndpoint} Test @@ -35,32 +39,31 @@ * @see ServiceRegistrationEndpoint * @since 1.0.0 */ -@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { - UtilAutoConfiguration.class, - SimpleDiscoveryClientAutoConfiguration.class, - CompositeDiscoveryClientAutoConfiguration.class + ServiceRegistrationEndpoint.class }) -@TestPropertySource( - properties = { - "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", - "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", - "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", - "spring.cloud.discovery.client.simple.instances.test[0].port=8080", - "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1" - } -) -class ServiceRegistrationEndpointTest { +public class ServiceRegistrationEndpointTest extends AbstractServiceRegistrationEndpointTest { - @BeforeEach - void setUp() { - } + @Autowired + private ServiceRegistrationEndpoint endpoint; @Test void testMetadata() { + Map metadata = this.endpoint.metadata(); + assertEquals("test-app", metadata.get("application-name")); + assertSame(this.registration, metadata.get("registration")); + assertEquals(this.port, metadata.get("port")); + assertNull(metadata.get("status")); + assertEquals(TRUE, metadata.get("running")); + assertEquals(TRUE, metadata.get("enabled")); + assertEquals(valueOf(0), metadata.get("phase")); + assertEquals(valueOf(0), metadata.get("order")); + assertSame(autoServiceRegistrationProperties, metadata.get("config")); } @Test void testStart() { + assertFalse(this.endpoint.start()); + assertTrue(this.endpoint.start()); } } \ No newline at end of file From 6d9aef296af8997f4ccfb6428cb95b98b26c76d7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 18:59:26 +0800 Subject: [PATCH 110/310] Add ServiceDeregistrationEndpoint unit test Introduces ServiceDeregistrationEndpointTest to verify the stop behavior of ServiceDeregistrationEndpoint and its interaction with ServiceRegistrationEndpoint. --- .../ServiceDeregistrationEndpointTest.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java new file mode 100644 index 00000000..1c7aa85f --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry.endpoint; + + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link ServiceDeregistrationEndpoint} Test + * + * @author Mercy + * @see ServiceDeregistrationEndpoint + * @since 1.0.0 + */ +@ContextConfiguration(classes = { + ServiceRegistrationEndpoint.class, + ServiceDeregistrationEndpoint.class +}) +class ServiceDeregistrationEndpointTest extends AbstractServiceRegistrationEndpointTest { + + @Autowired + private ServiceRegistrationEndpoint serviceRegistrationEndpoint; + + @Autowired + private ServiceDeregistrationEndpoint serviceDeregistrationEndpoint; + + @Test + void testStop() { + assertFalse(this.serviceDeregistrationEndpoint.stop()); + assertFalse(this.serviceRegistrationEndpoint.start()); + assertTrue(this.serviceDeregistrationEndpoint.stop()); + } +} \ No newline at end of file From 3b31bf6e1a6f556ee91773971c7df58ece7db754 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 20:00:27 +0800 Subject: [PATCH 111/310] Update ServiceRegistrationEndpoint.java --- .../endpoint/ServiceRegistrationEndpoint.java | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java index 500fe506..e3143b2e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java @@ -4,12 +4,13 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration; -import org.springframework.util.ReflectionUtils; -import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.Map; +import static io.microsphere.lang.function.ThrowableSupplier.execute; +import static io.microsphere.reflect.MethodUtils.invokeMethod; + /** * The {@link Endpoint @Endpoint} for Service Registration * @@ -29,17 +30,11 @@ public Map metadata() { metadata.put("port", port); metadata.put("status", serviceRegistry.getStatus(registration)); metadata.put("running", isRunning()); - - if (serviceRegistration != null) { - metadata.put("enabled", invoke("isEnabled")); - metadata.put("phase", serviceRegistration.getPhase()); - metadata.put("order", serviceRegistration.getOrder()); - if (Boolean.TRUE.equals(invoke("shouldRegisterManagement"))) { - metadata.put("managementRegistration", invoke("getManagementRegistration")); - } - metadata.put("config", invoke("getConfiguration")); - } - + metadata.put("enabled", invoke("isEnabled")); + metadata.put("phase", serviceRegistration.getPhase()); + metadata.put("order", serviceRegistration.getOrder()); + metadata.put("managementRegistration", invoke("getManagementRegistration")); + metadata.put("config", invoke("getConfiguration")); return metadata; } @@ -57,15 +52,6 @@ public boolean start() { } private Object invoke(String methodName) { - Object returnValue = null; - try { - Class serviceRegistrationClass = AbstractAutoServiceRegistration.class; - Method method = serviceRegistrationClass.getDeclaredMethod(methodName); - ReflectionUtils.makeAccessible(method); - returnValue = method.invoke(serviceRegistration); - } catch (Throwable e) { - logger.error("Invocation on method :" + methodName + "is failed", e); - } - return returnValue; + return execute(() -> invokeMethod(serviceRegistration, methodName), e -> null); } } From 298acb5edc29e8a3b05c7135136c4ca55baf9bdd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 20:00:47 +0800 Subject: [PATCH 112/310] Update ServiceRegistrationEndpoint.java --- .../service/registry/endpoint/ServiceRegistrationEndpoint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java index e3143b2e..2fafbff8 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java @@ -54,4 +54,4 @@ public boolean start() { private Object invoke(String methodName) { return execute(() -> invokeMethod(serviceRegistration, methodName), e -> null); } -} +} \ No newline at end of file From d9ca78db7d2b071fc3364db0296192f9560dd354 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 20:25:43 +0800 Subject: [PATCH 113/310] Move @EnableAutoConfiguration to concrete test classes Removed @EnableAutoConfiguration from AbstractServiceRegistrationEndpointTest and added it to ServiceRegistrationEndpointTest and ServiceDeregistrationEndpointTest. This change ensures auto-configuration is only applied to concrete test classes, improving test isolation and clarity. --- .../endpoint/AbstractServiceRegistrationEndpointTest.java | 1 - .../registry/endpoint/ServiceDeregistrationEndpointTest.java | 2 ++ .../registry/endpoint/ServiceRegistrationEndpointTest.java | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java index b78e2cde..181049a7 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java @@ -49,7 +49,6 @@ }, webEnvironment = RANDOM_PORT ) -@EnableAutoConfiguration class AbstractServiceRegistrationEndpointTest { @Autowired diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java index 1c7aa85f..f632984a 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.test.context.ContextConfiguration; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -36,6 +37,7 @@ ServiceRegistrationEndpoint.class, ServiceDeregistrationEndpoint.class }) +@EnableAutoConfiguration class ServiceDeregistrationEndpointTest extends AbstractServiceRegistrationEndpointTest { @Autowired diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java index 2f8cd269..b220d730 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.test.context.ContextConfiguration; import java.util.Map; @@ -42,6 +43,7 @@ @ContextConfiguration(classes = { ServiceRegistrationEndpoint.class }) +@EnableAutoConfiguration public class ServiceRegistrationEndpointTest extends AbstractServiceRegistrationEndpointTest { @Autowired From b5c0f6925ddda38dbe4d1004ec723233103666e6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 22:59:35 +0800 Subject: [PATCH 114/310] Refactor TomcatDynamicConfigurationListener logging and bean check Simplified logging by removing trace level checks and using direct trace calls. Updated bean presence check to use context in isBeanPresent. Removed redundant isBeanPresent method for clarity. --- .../TomcatDynamicConfigurationListener.java | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java index 968f55c0..1bfb35c5 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java @@ -18,7 +18,6 @@ import io.microsphere.logging.Logger; -import io.microsphere.logging.LoggerFactory; import org.apache.catalina.connector.Connector; import org.apache.coyote.AbstractProtocol; import org.apache.coyote.ProtocolHandler; @@ -38,6 +37,7 @@ import java.util.stream.Collectors; import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.spring.beans.BeanUtils.isBeanPresent; import static io.microsphere.spring.boot.context.properties.bind.util.BindUtils.bind; import static io.microsphere.spring.core.env.EnvironmentUtils.getProperties; import static io.microsphere.spring.core.env.PropertySourcesUtils.getSubProperties; @@ -76,7 +76,7 @@ public TomcatDynamicConfigurationListener(TomcatWebServer tomcatWebServer, Serve this.context = context; this.environment = environment; - this.configurationPropertiesRebinderPresent = isBeanPresent(ConfigurationPropertiesRebinder.class); + this.configurationPropertiesRebinderPresent = isBeanPresent(context, ConfigurationPropertiesRebinder.class); initCurrentServerProperties(); } @@ -85,24 +85,16 @@ private void initCurrentServerProperties() { this.currentServerProperties = getCurrentServerProperties(environment); } - private boolean isBeanPresent(Class beanType) { - return context.getBeanProvider(beanType).getIfAvailable() != null; - } - @Override public void onApplicationEvent(EnvironmentChangeEvent event) { if (!isSourceFrom(event)) { - if(logger.isTraceEnabled()) { - logger.trace("Current context[id : '{}'] receives the other changed property names : {}", context.getId(), event.getKeys()); - } + logger.trace("Current context[id : '{}'] receives the other changed property names : {}", context.getId(), event.getKeys()); return; } Set serverPropertyNames = filterServerPropertyNames(event); if (serverPropertyNames.isEmpty()) { - if(logger.isTraceEnabled()) { - logger.trace("Current context[id : '{}'] does not receive the property change of ServerProperties, keys : {}", context.getId(), event.getKeys()); - } + logger.trace("Current context[id : '{}'] does not receive the property change of ServerProperties, keys : {}", context.getId(), event.getKeys()); return; } @@ -128,9 +120,7 @@ private boolean isServerPropertyName(String propertyName) { private void configureTomcatIfChanged(Set serverPropertyNames) { ServerProperties refreshableServerProperties = getRefreshableServerProperties(serverPropertyNames); - if(logger.isTraceEnabled()) { - logger.debug("The ServerProperties property is changed to: {}", getProperties(environment, serverPropertyNames)); - } + logger.trace("The ServerProperties property is changed to: {}", getProperties(environment, serverPropertyNames)); configureConnector(refreshableServerProperties); // Reset current ServerProperties initCurrentServerProperties(); From ce35915f384d5320b4a30f50b628c226e7dd29f3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Tue, 21 Oct 2025 22:59:39 +0800 Subject: [PATCH 115/310] Add unit tests for TomcatDynamicConfigurationListener Introduces TomcatDynamicConfigurationListenerTest to verify event handling and dynamic configuration updates for Tomcat in the fault tolerance module. --- ...omcatDynamicConfigurationListenerTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java new file mode 100644 index 00000000..380cacd9 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.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 io.microsphere.spring.cloud.fault.tolerance.tomcat.event; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.mock.env.MockEnvironment; + +import static io.microsphere.collection.Sets.ofSet; + +/** + * {@link TomcatDynamicConfigurationListener} Testt + * + * @author Mercy + * @see TomcatDynamicConfigurationListener + * @since 1.0.0 + */ +class TomcatDynamicConfigurationListenerTest { + + private ServerProperties serverProperties; + + private MockEnvironment environment; + + private ConfigurableApplicationContext context; + + private TomcatDynamicConfigurationListener listener; + + @BeforeEach + void setUp() { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); + TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer(); + this.serverProperties = new ServerProperties(); + this.environment = new MockEnvironment(); + this.environment.setProperty("server.port", "8080"); + this.context = new GenericApplicationContext(); + this.context.setEnvironment(this.environment); + this.context.refresh(); + this.listener = new TomcatDynamicConfigurationListener(tomcatWebServer, this.serverProperties, this.context); + } + + @Test + void testOnApplicationEventOnDifferentEventSource() { + EnvironmentChangeEvent event = new EnvironmentChangeEvent(ofSet("test-key")); + this.listener.onApplicationEvent(event); + } + + @Test + void testOnApplicationEventOnEmptyKeys() { + EnvironmentChangeEvent event = new EnvironmentChangeEvent(this.context, ofSet()); + this.listener.onApplicationEvent(event); + } + + @Test + void testOnApplicationEvent() { + String key = "server.tomcat.threads.max"; + this.environment.setProperty(key, "100"); + EnvironmentChangeEvent event = new EnvironmentChangeEvent(this.context, ofSet(key)); + this.listener.onApplicationEvent(event); + } + +} \ No newline at end of file From 7664b18767b31558b1033f66f2888aab56c64a0e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 09:22:49 +0800 Subject: [PATCH 116/310] Increase method visibility for test coverage Changed private methods in TomcatDynamicConfigurationListener to package-private to allow direct testing. Added unit tests for configureProtocol, configureHttp11Protocol, and isPositive methods to improve coverage. --- .../event/TomcatDynamicConfigurationListener.java | 9 ++++----- .../TomcatDynamicConfigurationListenerTest.java | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java index 1bfb35c5..10daa3c4 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java @@ -146,7 +146,7 @@ private void configureConnector(ServerProperties refreshableServerProperties) { configureHttp11Protocol(refreshableServerProperties, connector, protocolHandler); } - private void configureProtocol(ServerProperties refreshableServerProperties, ProtocolHandler protocolHandler) { + void configureProtocol(ServerProperties refreshableServerProperties, ProtocolHandler protocolHandler) { if (protocolHandler instanceof AbstractProtocol) { ServerProperties.Tomcat refreshableTomcatProperties = refreshableServerProperties.getTomcat(); @@ -194,7 +194,7 @@ private void configureProtocol(ServerProperties refreshableServerProperties, Pro } } - private void configureHttp11Protocol(ServerProperties refreshableServerProperties, Connector connector, ProtocolHandler protocolHandler) { + void configureHttp11Protocol(ServerProperties refreshableServerProperties, Connector connector, ProtocolHandler protocolHandler) { if (protocolHandler instanceof AbstractHttp11Protocol) { AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) protocolHandler; @@ -236,9 +236,8 @@ private int toIntBytes(DataSize dataSize) { return (int) dataSize.toBytes(); } - private boolean isPositive(int value) { + boolean isPositive(int value) { return value > 0; } - -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java index 380cacd9..58d3f3be 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java @@ -80,4 +80,18 @@ void testOnApplicationEvent() { this.listener.onApplicationEvent(event); } + @Test + void testConfigureProtocolWithNull() { + this.listener.configureProtocol(this.serverProperties, null); + } + + @Test + void testConfigureHttp11ProtocolWithNull() { + this.listener.configureHttp11Protocol(this.serverProperties, null, null); + } + + @Test + void testIsPositiveWithNegative() { + this.listener.isPositive(-1); + } } \ No newline at end of file From 35e4c5bf02a23a8f8a17cb5e49c970a28b01463e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 10:09:37 +0800 Subject: [PATCH 117/310] Refactor property constants and update tests Moved DiscoveryClient property constants from DiscoveryClientConstants interface to DiscoveryClientAutoConfiguration class. Updated TomcatFaultToleranceAutoConfiguration to define ENABLED_PROPERTY_NAME with @ConfigurationProperty annotation. Adjusted related test classes to reference new constant locations and added additional test coverage for TomcatFaultToleranceAutoConfiguration event handling. --- .../DiscoveryClientAutoConfiguration.java | 29 ++++++++++++++-- .../constants/DiscoveryClientConstants.java | 28 ---------------- ...TomcatFaultToleranceAutoConfiguration.java | 21 +++++++++--- .../DiscoveryClientAutoConfigurationTest.java | 10 ++++++ .../DiscoveryClientConstantsTest.java | 7 ---- ...atFaultToleranceAutoConfigurationTest.java | 33 +++++++++++++++++-- 6 files changed, 84 insertions(+), 44 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java index 88e2290c..101a6413 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java @@ -16,6 +16,7 @@ */ package io.microsphere.spring.cloud.client.discovery.autoconfigure; +import io.microsphere.annotation.ConfigurationProperty; import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @@ -28,10 +29,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; -import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_MODE_PROPERTY_NAME; -import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.UNION_DISCOVERY_CLIENT_MODE; +import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX; /** * {@link UnionDiscoveryClient} Auto-Configuration Class @@ -52,6 +53,30 @@ }) public class DiscoveryClientAutoConfiguration { + /** + * The property prefix of {@link DiscoveryClient} : "microsphere.spring.cloud.client.discovery." + */ + public static final String DISCOVERY_CLIENT_PROPERTY_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "client.discovery."; + + /** + * The property name of mode : "mode" + */ + public static final String MODE_PROPERTY_NAME = "mode"; + + /** + * The {@link DiscoveryClient} "mode" for {@link UnionDiscoveryClient} : "union" + */ + public static final String UNION_DISCOVERY_CLIENT_MODE = "union"; + + /** + * The property name of DiscoveryClient mode : "microsphere.spring.cloud.client.discovery.mode" + */ + @ConfigurationProperty( + source = APPLICATION_SOURCE + ) + public static final String DISCOVERY_CLIENT_MODE_PROPERTY_NAME = DISCOVERY_CLIENT_PROPERTY_PREFIX + MODE_PROPERTY_NAME; + + @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = DISCOVERY_CLIENT_MODE_PROPERTY_NAME, havingValue = UNION_DISCOVERY_CLIENT_MODE) public static class UnionConfiguration { diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java index e1a11086..f5b9f10c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java @@ -16,15 +16,10 @@ */ package io.microsphere.spring.cloud.client.discovery.constants; -import io.microsphere.annotation.ConfigurationProperty; -import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; import org.springframework.cloud.client.CommonsClientAutoConfiguration; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; -import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; -import static io.microsphere.spring.cloud.commons.constants.CommonsPropertyConstants.MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX; - /** * The constants for {@link DiscoveryClient} * @@ -33,29 +28,6 @@ */ public interface DiscoveryClientConstants { - /** - * The property prefix of {@link DiscoveryClient} : "microsphere.spring.cloud.client.discovery." - */ - String DISCOVERY_CLIENT_PROPERTY_PREFIX = MICROSPHERE_SPRING_CLOUD_PROPERTY_NAME_PREFIX + "client.discovery."; - - /** - * The property name of mode : "mode" - */ - String MODE_PROPERTY_NAME = "mode"; - - /** - * The {@link DiscoveryClient} "mode" for {@link UnionDiscoveryClient} : "union" - */ - String UNION_DISCOVERY_CLIENT_MODE = "union"; - - /** - * The property name of DiscoveryClient mode : "microsphere.spring.cloud.client.discovery.mode" - */ - @ConfigurationProperty( - source = APPLICATION_SOURCE - ) - String DISCOVERY_CLIENT_MODE_PROPERTY_NAME = DISCOVERY_CLIENT_PROPERTY_PREFIX + MODE_PROPERTY_NAME; - /** * The class name of {@link DiscoveryClient} */ diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java index b4ee6190..ea12ae9a 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java @@ -16,7 +16,8 @@ */ package io.microsphere.spring.cloud.fault.tolerance.tomcat.autoconfigure; -import io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants; +import io.microsphere.annotation.ConfigurationProperty; +import io.microsphere.constants.PropertyConstants; import io.microsphere.spring.cloud.fault.tolerance.tomcat.event.TomcatDynamicConfigurationListener; import org.apache.catalina.startup.Tomcat; import org.springframework.beans.factory.ObjectProvider; @@ -35,8 +36,9 @@ import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.event.EventListener; -import static io.microsphere.constants.PropertyConstants.ENABLED_PROPERTY_NAME; -import static io.microsphere.spring.cloud.fault.tolerance.tomcat.autoconfigure.TomcatFaultToleranceAutoConfiguration.TOMCAT_PROPERTY_PREFIX; +import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; +import static io.microsphere.spring.cloud.fault.tolerance.constants.FaultTolerancePropertyConstants.FAULT_TOLERANCE_PROPERTY_NAME_PREFIX; +import static io.microsphere.spring.cloud.fault.tolerance.tomcat.autoconfigure.TomcatFaultToleranceAutoConfiguration.ENABLED_PROPERTY_NAME; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; /** @@ -44,7 +46,6 @@ * @since 1.0.0 */ @ConditionalOnProperty( - prefix = TOMCAT_PROPERTY_PREFIX, name = ENABLED_PROPERTY_NAME, matchIfMissing = true ) @@ -59,7 +60,17 @@ }) public class TomcatFaultToleranceAutoConfiguration { - public static final String TOMCAT_PROPERTY_PREFIX = FaultTolerancePropertyConstants.FAULT_TOLERANCE_PROPERTY_NAME_PREFIX + "tomcat"; + public static final String TOMCAT_PROPERTY_PREFIX = FAULT_TOLERANCE_PROPERTY_NAME_PREFIX + "tomcat"; + + /** + * The property name to Tomcat's fault-tolerance enabled or not: "microsphere.spring.cloud.fault-tolerance.tomcat.enabled" + */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "true", + source = APPLICATION_SOURCE + ) + public static final String ENABLED_PROPERTY_NAME = TOMCAT_PROPERTY_PREFIX + "." + PropertyConstants.ENABLED_PROPERTY_NAME; @EventListener(WebServerInitializedEvent.class) public void onWebServerInitializedEvent(WebServerInitializedEvent event) { diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java index 8d5746e2..76fdc403 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfigurationTest.java @@ -33,6 +33,9 @@ import java.util.Arrays; import java.util.List; +import static io.microsphere.spring.cloud.client.discovery.autoconfigure.DiscoveryClientAutoConfiguration.DISCOVERY_CLIENT_PROPERTY_PREFIX; +import static io.microsphere.spring.cloud.client.discovery.autoconfigure.DiscoveryClientAutoConfiguration.MODE_PROPERTY_NAME; +import static io.microsphere.spring.cloud.client.discovery.autoconfigure.DiscoveryClientAutoConfiguration.UNION_DISCOVERY_CLIENT_MODE; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -64,6 +67,13 @@ class DiscoveryClientAutoConfigurationTest { @Autowired private DiscoveryClient discoveryClient; + @Test + void testConstants() { + assertEquals("microsphere.spring.cloud.client.discovery.", DISCOVERY_CLIENT_PROPERTY_PREFIX); + assertEquals("mode", MODE_PROPERTY_NAME); + assertEquals("union", UNION_DISCOVERY_CLIENT_MODE); + } + @Test void test() { assertEquals(CompositeDiscoveryClient.class, discoveryClient.getClass()); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java index 5a9721c7..cadb2c57 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java @@ -21,9 +21,6 @@ import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; -import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_PROPERTY_PREFIX; -import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.MODE_PROPERTY_NAME; -import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.UNION_DISCOVERY_CLIENT_MODE; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -37,10 +34,6 @@ class DiscoveryClientConstantsTest { @Test void testConstants() { - assertEquals("microsphere.spring.cloud.client.discovery.", DISCOVERY_CLIENT_PROPERTY_PREFIX); - assertEquals("mode", MODE_PROPERTY_NAME); - assertEquals("union", UNION_DISCOVERY_CLIENT_MODE); - assertEquals("org.springframework.cloud.client.discovery.DiscoveryClient", DISCOVERY_CLIENT_CLASS_NAME); assertEquals("org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient", COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME); assertEquals("org.springframework.cloud.client.CommonsClientAutoConfiguration", COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java index ca00aea0..189f2013 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java @@ -25,10 +25,15 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.context.WebServerApplicationContext; +import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; +import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; import org.springframework.cloud.context.environment.EnvironmentManager; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; /** * {@link TomcatFaultToleranceAutoConfiguration} Test @@ -48,6 +53,11 @@ class TomcatFaultToleranceAutoConfigurationTest { @Autowired private WebServerApplicationContext context; + @Autowired + private TomcatFaultToleranceAutoConfiguration configuration; + + private TomcatWebServer tomcatWebServer; + private Tomcat tomcat; private Connector connector; @@ -56,8 +66,8 @@ class TomcatFaultToleranceAutoConfigurationTest { @BeforeEach void before() { - TomcatWebServer tomcatWebServer = (TomcatWebServer) context.getWebServer(); - this.tomcat = tomcatWebServer.getTomcat(); + this.tomcatWebServer = (TomcatWebServer) context.getWebServer(); + this.tomcat = this.tomcatWebServer.getTomcat(); this.connector = tomcat.getConnector(); this.protocol = (AbstractHttp11Protocol) connector.getProtocolHandler(); } @@ -133,4 +143,23 @@ void testMaxHttpFormPostSize() { environmentManager.setProperty("server.tomcat.max-http-form-post-size", "10240"); assertEquals(10240, connector.getMaxPostSize()); } + + @Test + void testOnWebServerInitializedEventWithWebServerInitializedEvent() { + WebServerInitializedEvent event = new WebServerInitializedEvent(this.tomcatWebServer) { + @Override + public WebServerApplicationContext getApplicationContext() { + return null; + } + }; + configuration.onWebServerInitializedEvent(event); + } + + @Test + void testOnWebServerInitializedEventWithNonTomcatWebServer() { + ServletWebServerApplicationContext context = (ServletWebServerApplicationContext) this.context; + WebServer webServer = mock(WebServer.class); + WebServerInitializedEvent event = new ServletWebServerInitializedEvent(webServer, context); + configuration.onWebServerInitializedEvent(event); + } } From 1a4ef0aa0cdecc030919537c1aa5440dfc573ea1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 10:11:52 +0800 Subject: [PATCH 118/310] Update AbstractServiceRegistrationEndpointTest.java --- .../endpoint/AbstractServiceRegistrationEndpointTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java index 181049a7..9861773a 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; From 9e692d8cda1f5215d601a745da2abaa51c552f4b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 14:31:13 +0800 Subject: [PATCH 119/310] Refactor service registration endpoint tests AbstractServiceRegistrationEndpoint is now abstract and its test class logic is split. Introduced BaseServiceRegistrationEndpointTest to share test setup, updated related test classes to extend the new base, and fixed running state logic in AbstractServiceRegistrationEndpoint. --- .../AbstractServiceRegistrationEndpoint.java | 4 +- ...stractServiceRegistrationEndpointTest.java | 61 ++++++++----------- .../BaseServiceRegistrationEndpointTest.java | 61 +++++++++++++++++++ .../ServiceDeregistrationEndpointTest.java | 2 +- .../ServiceRegistrationEndpointTest.java | 2 +- 5 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java index 8562f06e..c5e8eb6c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java @@ -20,7 +20,7 @@ * @author Mercy * @since 1.0.0 */ -public class AbstractServiceRegistrationEndpoint implements SmartInitializingSingleton, ApplicationListener { +public abstract class AbstractServiceRegistrationEndpoint implements SmartInitializingSingleton, ApplicationListener { protected final Logger logger = getLogger(getClass()); @@ -57,7 +57,7 @@ public void afterSingletonsInstantiated() { public void onApplicationEvent(WebServerInitializedEvent event) { WebServer webServer = event.getWebServer(); this.port = webServer.getPort(); - this.running = serviceRegistration == null ? true : serviceRegistration.isRunning(); + this.running = serviceRegistration == null ? false : serviceRegistration.isRunning(); } protected boolean isRunning() { diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java index 9861773a..b1840e79 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java @@ -17,45 +17,38 @@ package io.microsphere.spring.cloud.client.service.registry.endpoint; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; +import org.springframework.test.context.ContextConfiguration; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** - * {@link AbstractServiceRegistrationEndpoint} Base Test + * {@link AbstractServiceRegistrationEndpoint} Test * * @author Mercy - * @see AbstractServiceRegistrationEndpoint + * @see BaseServiceRegistrationEndpointTest * @since 1.0.0 */ -@ExtendWith(SpringExtension.class) -@SpringBootTest( - properties = { - "spring.application.name=test-app", - "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", - "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", - "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", - "spring.cloud.discovery.client.simple.instances.test[0].port=8080", - "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1", - "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true" - }, - webEnvironment = RANDOM_PORT -) -class AbstractServiceRegistrationEndpointTest { - - @Autowired - protected Registration registration; - - @LocalServerPort - protected Integer port; - - @Autowired - protected AutoServiceRegistrationProperties autoServiceRegistrationProperties; +@ContextConfiguration(classes = { + AbstractServiceRegistrationEndpointTest.class +}) +@EnableAutoConfiguration +class AbstractServiceRegistrationEndpointTest extends BaseServiceRegistrationEndpointTest { + + @Test + void testOnApplicationEvent() { + AbstractServiceRegistrationEndpoint endpoint = new AbstractServiceRegistrationEndpoint() { + }; + WebServer webServer = mock(WebServer.class); + when(webServer.getPort()).thenReturn(this.port); + WebServerInitializedEvent event = new ServletWebServerInitializedEvent(webServer, null); + endpoint.onApplicationEvent(event); + assertFalse(endpoint.isRunning()); + } } \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java new file mode 100644 index 00000000..cbd55d63 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.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 io.microsphere.spring.cloud.client.service.registry.endpoint; + + +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * {@link AbstractServiceRegistrationEndpoint} Base Test + * + * @author Mercy + * @see AbstractServiceRegistrationEndpoint + * @since 1.0.0 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest( + properties = { + "spring.application.name=test-app", + "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", + "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", + "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", + "spring.cloud.discovery.client.simple.instances.test[0].port=8080", + "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1", + "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true" + }, + webEnvironment = RANDOM_PORT +) +class BaseServiceRegistrationEndpointTest { + + @Autowired + protected Registration registration; + + @LocalServerPort + protected Integer port; + + @Autowired + protected AutoServiceRegistrationProperties autoServiceRegistrationProperties; +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java index f632984a..8320b8cd 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java @@ -38,7 +38,7 @@ ServiceDeregistrationEndpoint.class }) @EnableAutoConfiguration -class ServiceDeregistrationEndpointTest extends AbstractServiceRegistrationEndpointTest { +class ServiceDeregistrationEndpointTest extends BaseServiceRegistrationEndpointTest { @Autowired private ServiceRegistrationEndpoint serviceRegistrationEndpoint; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java index b220d730..8f5b3eb9 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java @@ -44,7 +44,7 @@ ServiceRegistrationEndpoint.class }) @EnableAutoConfiguration -public class ServiceRegistrationEndpointTest extends AbstractServiceRegistrationEndpointTest { +public class ServiceRegistrationEndpointTest extends BaseServiceRegistrationEndpointTest { @Autowired private ServiceRegistrationEndpoint endpoint; From 3626fa1dd68a2c3ceb2dadc7b7b739df6d0e6ffc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 15:09:06 +0800 Subject: [PATCH 120/310] Refactor aspect logic and add unit tests Extracted registry type check into isIgnored() method in EventPublishingRegistrationAspect for clarity and maintainability. Added EventPublishingRegistrationAspectTest to verify event publishing behavior during register and deregister operations. --- .../EventPublishingRegistrationAspect.java | 19 ++- ...EventPublishingRegistrationAspectTest.java | 115 ++++++++++++++++++ 2 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java index 57a45a06..5251b0f4 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java @@ -71,25 +71,32 @@ public void beforeRegister(ServiceRegistry registry, Registration registration) @Before(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeDeregister(ServiceRegistry registry, Registration registration) { - if (registry.getClass().isAssignableFrom(MultipleServiceRegistry.class)) - return;//Remove redundant deregister + if (isIgnored(registry)) { + return; // Remove redundant deregister + } context.publishEvent(new RegistrationPreDeregisteredEvent(registry, registration)); } @After(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void afterRegister(ServiceRegistry registry, Registration registration) { - if (registry.getClass().isAssignableFrom(MultipleServiceRegistry.class)) - return;//Remove redundant register + if (isIgnored(registry)) { + return; // Remove redundant deregister + } context.publishEvent(new RegistrationRegisteredEvent(registry, registration)); } @After(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void afterDeregister(ServiceRegistry registry, Registration registration) { - if (registry.getClass().isAssignableFrom(MultipleServiceRegistry.class)) - return;//Remove redundant deregister + if (isIgnored(registry)) { + return; // Remove redundant deregister + } context.publishEvent(new RegistrationDeregisteredEvent(registry, registration)); } + boolean isIgnored(ServiceRegistry registry) { + return MultipleServiceRegistry.class.isAssignableFrom(registry.getClass()); + } + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java new file mode 100644 index 00000000..a2130667 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry.aspect; + + +import io.microsphere.spring.cloud.client.service.registry.DefaultRegistration; +import io.microsphere.spring.cloud.client.service.registry.InMemoryServiceRegistry; +import io.microsphere.spring.cloud.client.service.registry.RegistrationCustomizer; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationDeregisteredEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationPreDeregisteredEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationPreRegisteredEvent; +import io.microsphere.spring.cloud.client.service.registry.event.RegistrationRegisteredEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static java.lang.System.currentTimeMillis; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link EventPublishingRegistrationAspect} Test + * + * @author Mercy + * @see EventPublishingRegistrationAspect + * @since 1.0.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + InMemoryServiceRegistry.class, + EventPublishingRegistrationAspect.class, + EventPublishingRegistrationAspectTest.RegistrationCustomizerImpl.class, + EventPublishingRegistrationAspectTest.class, +}) +@DirtiesContext +@EnableAspectJAutoProxy +class EventPublishingRegistrationAspectTest { + + @Autowired + private ServiceRegistry serviceRegistry; + + @Autowired + private ConfigurableApplicationContext context; + + private Registration registration; + + @BeforeEach + void setUp() { + DefaultRegistration defaultRegistration = new DefaultRegistration(); + defaultRegistration.setInstanceId("ServiceInstance-" + currentTimeMillis()); + defaultRegistration.setServiceId("test-service"); + defaultRegistration.setHost("localhost"); + defaultRegistration.setPort(8080); + defaultRegistration.setSecure(false); + this.registration = defaultRegistration; + } + + @Test + void testBeforeAndAfterRegister() { + this.context.addApplicationListener((ApplicationListener) event -> { + assertRegistration(event); + }); + this.context.addApplicationListener((ApplicationListener) event -> { + assertRegistration(event); + }); + this.serviceRegistry.register(registration); + } + + @Test + void testBeforeAndAfterDeregister() { + this.context.addApplicationListener((ApplicationListener) event -> { + assertRegistration(event); + }); + this.context.addApplicationListener((ApplicationListener) event -> { + assertRegistration(event); + }); + this.serviceRegistry.deregister(registration); + } + + void assertRegistration(RegistrationEvent event) { + assertSame(this.registration, event.getRegistration()); + } + + + static class RegistrationCustomizerImpl implements RegistrationCustomizer { + + @Override + public void customize(Registration registration) { + } + } +} \ No newline at end of file From cee436a8a2edd8e9774a91a56a151e2a12758983 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 17:18:07 +0800 Subject: [PATCH 121/310] Refactor registry type check in aspect Replaced direct class comparison with isIgnored(registry) method in EventPublishingRegistrationAspect to improve readability and maintainability. Updated comment to clarify intent. --- .../registry/aspect/EventPublishingRegistrationAspect.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java index 5251b0f4..d6112a08 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java @@ -61,8 +61,9 @@ public class EventPublishingRegistrationAspect implements ApplicationContextAwar @Before(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeRegister(ServiceRegistry registry, Registration registration) { - if (registry.getClass().isAssignableFrom(MultipleServiceRegistry.class)) - return;//Remove redundant register + if (isIgnored(registry)) { + return; // Remove redundant deregister + } context.publishEvent(new RegistrationPreRegisteredEvent(registry, registration)); registrationCustomizers.forEach(customizer -> { customizer.customize(registration); From 18fc7aa93a477906567ad3aa0c157abae60c329a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 17:18:13 +0800 Subject: [PATCH 122/310] Refactor tests to use MultipleRegistration and MultipleServiceRegistry Updated EventPublishingRegistrationAspectTest to use MultipleRegistration and MultipleServiceRegistry instead of single registration and registry. Added configuration class to provide beans and enable AspectJ auto-proxying for improved test coverage of multiple registration scenarios. --- ...EventPublishingRegistrationAspectTest.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java index a2130667..5b00b9ab 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java @@ -20,6 +20,8 @@ import io.microsphere.spring.cloud.client.service.registry.DefaultRegistration; import io.microsphere.spring.cloud.client.service.registry.InMemoryServiceRegistry; +import io.microsphere.spring.cloud.client.service.registry.MultipleRegistration; +import io.microsphere.spring.cloud.client.service.registry.MultipleServiceRegistry; import io.microsphere.spring.cloud.client.service.registry.RegistrationCustomizer; import io.microsphere.spring.cloud.client.service.registry.event.RegistrationDeregisteredEvent; import io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent; @@ -34,11 +36,17 @@ import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.Map; + +import static io.microsphere.collection.Lists.ofList; import static java.lang.System.currentTimeMillis; import static org.junit.jupiter.api.Assertions.assertSame; @@ -51,13 +59,10 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { - InMemoryServiceRegistry.class, EventPublishingRegistrationAspect.class, - EventPublishingRegistrationAspectTest.RegistrationCustomizerImpl.class, - EventPublishingRegistrationAspectTest.class, + EventPublishingRegistrationAspectTest.Config.class }) @DirtiesContext -@EnableAspectJAutoProxy class EventPublishingRegistrationAspectTest { @Autowired @@ -66,7 +71,7 @@ class EventPublishingRegistrationAspectTest { @Autowired private ConfigurableApplicationContext context; - private Registration registration; + private MultipleRegistration multipleRegistration; @BeforeEach void setUp() { @@ -76,7 +81,7 @@ void setUp() { defaultRegistration.setHost("localhost"); defaultRegistration.setPort(8080); defaultRegistration.setSecure(false); - this.registration = defaultRegistration; + this.multipleRegistration = new MultipleRegistration(ofList(defaultRegistration)); } @Test @@ -87,7 +92,7 @@ void testBeforeAndAfterRegister() { this.context.addApplicationListener((ApplicationListener) event -> { assertRegistration(event); }); - this.serviceRegistry.register(registration); + this.serviceRegistry.register(multipleRegistration); } @Test @@ -98,18 +103,25 @@ void testBeforeAndAfterDeregister() { this.context.addApplicationListener((ApplicationListener) event -> { assertRegistration(event); }); - this.serviceRegistry.deregister(registration); + this.serviceRegistry.deregister(multipleRegistration); } void assertRegistration(RegistrationEvent event) { - assertSame(this.registration, event.getRegistration()); + assertSame(this.multipleRegistration, event.getRegistration()); } - - static class RegistrationCustomizerImpl implements RegistrationCustomizer { + @Import(InMemoryServiceRegistry.class) + @EnableAspectJAutoProxy + static class Config implements RegistrationCustomizer { @Override public void customize(Registration registration) { } + + @Bean + @Primary + public MultipleServiceRegistry multipleServiceRegistry(Map registriesMap) { + return new MultipleServiceRegistry(registriesMap); + } } } \ No newline at end of file From 78e56729594895c545b7009e4de56d89ed793105 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 17:35:20 +0800 Subject: [PATCH 123/310] Refactor test registration creation utility Moved the createDefaultRegistration() method from MultipleRegistrationTest to DefaultRegistrationTest for reuse. Updated all test classes to import and use the new location, improving code reuse and consistency in test setup. --- .../registry/DefaultRegistrationTest.java | 149 ++++++++++++++++++ .../MultipleAutoServiceRegistrationTest.java | 2 +- .../registry/MultipleRegistrationTest.java | 12 +- .../registry/MultipleServiceRegistryTest.java | 2 +- .../registry/RegistrationMetaDataTest.java | 2 +- .../SimpleAutoServiceRegistrationTest.java | 3 +- ...EventPublishingRegistrationAspectTest.java | 9 +- .../ServiceRegistryAutoConfigurationTest.java | 7 +- 8 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java new file mode 100644 index 00000000..a132e5e2 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.URI; + +import static java.lang.System.currentTimeMillis; +import static java.net.URI.create; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link DefaultRegistration} Test + * + * @author Mercy + * @see DefaultRegistration + * @since 1.0.0 + */ +public class DefaultRegistrationTest { + + private DefaultRegistration registration; + + @BeforeEach + void setUp() { + this.registration = createDefaultRegistration(); + } + + @Test + void testGetUri() { + assertNotNull(this.registration.getUri()); + } + + @Test + void testGetMetadata() { + assertNotNull(this.registration.getMetadata()); + } + + @Test + void testGetInstanceId() { + assertNotNull(this.registration.getInstanceId()); + } + + @Test + void testGetServiceId() { + assertEquals("test-service", this.registration.getServiceId()); + } + + @Test + void testGetHost() { + assertEquals("localhost", this.registration.getHost()); + } + + @Test + void testGetPort() { + assertEquals(8080, this.registration.getPort()); + } + + @Test + void testIsSecure() { + assertFalse(this.registration.isSecure()); + } + + @Test + void testSetInstanceId() { + this.registration.setInstanceId("test-instance-id"); + assertEquals("test-instance-id", this.registration.getInstanceId()); + } + + @Test + void testSetServiceId() { + this.registration.setServiceId("test-service-id"); + assertEquals("test-service-id", this.registration.getServiceId()); + } + + @Test + void testSetHost() { + this.registration.setHost("test-host"); + assertEquals("test-host", this.registration.getHost()); + } + + @Test + void testSetPort() { + this.registration.setPort(9090); + assertEquals(9090, this.registration.getPort()); + } + + @Test + void testSetUri() { + URI uri = create("http://localhost:9090"); + this.registration.setUri(uri); + assertEquals(uri, this.registration.getUri()); + } + + @Test + void testToString() { + assertNotNull(this.registration.toString()); + } + + @Test + void testEquals() { + DefaultRegistration registration = createDefaultRegistration(); + registration.setInstanceId(this.registration.getInstanceId()); + assertEquals(this.registration, registration); + } + + @Test + void testHashCode() { + DefaultRegistration registration = createDefaultRegistration(); + registration.setInstanceId(this.registration.getInstanceId()); + assertEquals(this.registration.hashCode(), registration.hashCode()); + + } + + @Test + void testGetScheme() { + assertNull(this.registration.getScheme()); + } + + public static DefaultRegistration createDefaultRegistration() { + DefaultRegistration defaultRegistration = new DefaultRegistration(); + defaultRegistration.setInstanceId("ServiceInstance-" + currentTimeMillis()); + defaultRegistration.setServiceId("test-service"); + defaultRegistration.setHost("localhost"); + defaultRegistration.setPort(8080); + defaultRegistration.setSecure(true); + return defaultRegistration; + } +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.java index 0bb5163b..ac5f1520 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistrationTest.java @@ -24,7 +24,7 @@ import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.spring.cloud.client.service.registry.MultipleRegistrationTest.createDefaultRegistration; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java index 4c92a12d..5f8d08d5 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistrationTest.java @@ -23,7 +23,7 @@ import org.springframework.cloud.client.serviceregistry.Registration; import static io.microsphere.collection.Lists.ofList; -import static java.lang.System.currentTimeMillis; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -46,16 +46,6 @@ void setUp() { this.registration = new MultipleRegistration(ofList(defaultRegistration)); } - static DefaultRegistration createDefaultRegistration() { - DefaultRegistration defaultRegistration = new DefaultRegistration(); - defaultRegistration.setInstanceId("ServiceInstance-" + currentTimeMillis()); - defaultRegistration.setServiceId("test-service"); - defaultRegistration.setHost("localhost"); - defaultRegistration.setPort(8080); - defaultRegistration.setSecure(false); - return defaultRegistration; - } - @Test void testGetInstanceId() { assertEquals(defaultRegistration.getInstanceId(), registration.getInstanceId()); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java index 5b4fcbf2..0bef8aaa 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistryTest.java @@ -27,7 +27,7 @@ import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.Maps.ofMap; -import static io.microsphere.spring.cloud.client.service.registry.MultipleRegistrationTest.createDefaultRegistration; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static io.microsphere.spring.cloud.client.service.registry.MultipleServiceRegistry.getRegistrationClass; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java index 0c06892a..7239edad 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaDataTest.java @@ -28,7 +28,7 @@ import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.MapUtils.ofEntry; import static io.microsphere.collection.Maps.ofMap; -import static io.microsphere.spring.cloud.client.service.registry.MultipleRegistrationTest.createDefaultRegistration; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java index 00fe5776..218a2f90 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistrationTest.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static org.junit.jupiter.api.Assertions.assertSame; /** @@ -45,7 +46,7 @@ class SimpleAutoServiceRegistrationTest { void setUp() { this.serviceRegistry = new InMemoryServiceRegistry(); this.properties = new AutoServiceRegistrationProperties(); - this.registration = new DefaultRegistration(); + this.registration = createDefaultRegistration(); this.autoServiceRegistration = new SimpleAutoServiceRegistration(serviceRegistry, properties, registration); } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java index 5b00b9ab..cbdfc790 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspectTest.java @@ -47,7 +47,7 @@ import java.util.Map; import static io.microsphere.collection.Lists.ofList; -import static java.lang.System.currentTimeMillis; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static org.junit.jupiter.api.Assertions.assertSame; /** @@ -75,12 +75,7 @@ class EventPublishingRegistrationAspectTest { @BeforeEach void setUp() { - DefaultRegistration defaultRegistration = new DefaultRegistration(); - defaultRegistration.setInstanceId("ServiceInstance-" + currentTimeMillis()); - defaultRegistration.setServiceId("test-service"); - defaultRegistration.setHost("localhost"); - defaultRegistration.setPort(8080); - defaultRegistration.setSecure(false); + DefaultRegistration defaultRegistration = createDefaultRegistration(); this.multipleRegistration = new MultipleRegistration(ofList(defaultRegistration)); } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java index 28eaf1b9..9e1a0091 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfigurationTest.java @@ -1,6 +1,5 @@ package io.microsphere.spring.cloud.client.service.registry.autoconfigure; -import io.microsphere.spring.cloud.client.service.registry.DefaultRegistration; import io.microsphere.spring.cloud.client.service.registry.InMemoryServiceRegistry; import io.microsphere.spring.cloud.client.service.registry.event.RegistrationDeregisteredEvent; import io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent; @@ -17,6 +16,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.EnableAspectJAutoProxy; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.DEREGISTERED; import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_DEREGISTERED; import static io.microsphere.spring.cloud.client.service.registry.event.RegistrationEvent.Type.PRE_REGISTERED; @@ -57,10 +57,7 @@ class ServiceRegistryAutoConfigurationTest { @BeforeEach void setUp() { - DefaultRegistration registration = new DefaultRegistration(); - registration.setServiceId("test-service"); - registration.setInstanceId("127.0.0.1:8080"); - this.registration = registration; + this.registration = createDefaultRegistration(); } @Test From 5791d9a4336668e58dfd2d0e3f8e65e6fc9fbddd Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 17:39:03 +0800 Subject: [PATCH 124/310] Update DefaultRegistrationTest.java --- .../client/service/registry/DefaultRegistrationTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java index a132e5e2..ffa974e5 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistrationTest.java @@ -26,9 +26,9 @@ import static java.lang.System.currentTimeMillis; import static java.net.URI.create; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link DefaultRegistration} Test @@ -78,7 +78,7 @@ void testGetPort() { @Test void testIsSecure() { - assertFalse(this.registration.isSecure()); + assertTrue(this.registration.isSecure()); } @Test @@ -107,7 +107,7 @@ void testSetPort() { @Test void testSetUri() { - URI uri = create("http://localhost:9090"); + URI uri = create("https://localhost:9090"); this.registration.setUri(uri); assertEquals(uri, this.registration.getUri()); } From 7db8230a568d235e8b2923869ef059b72efdc442 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 19:41:30 +0800 Subject: [PATCH 125/310] Refactor UnionDiscoveryClient to use ApplicationContext UnionDiscoveryClient now implements ApplicationContextAware and retrieves DiscoveryClient beans directly from the context, removing the need for ObjectProvider injection. The auto-configuration and related tests have been updated to reflect this change, improving bean sorting and test coverage. --- .../discovery/UnionDiscoveryClient.java | 27 ++++--- .../DiscoveryClientAutoConfiguration.java | 5 +- .../discovery/UnionDiscoveryClientTest.java | 72 +++++++++++++++++-- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java index bf517416..f23f0987 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java @@ -16,19 +16,23 @@ */ package io.microsphere.spring.cloud.client.discovery; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import static io.microsphere.collection.CollectionUtils.isNotEmpty; import static io.microsphere.reflect.TypeUtils.getClassName; +import static io.microsphere.spring.beans.BeanUtils.getSortedBeans; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME; /** @@ -38,16 +42,12 @@ * @see CompositeDiscoveryClient * @since 1.0.0 */ -public final class UnionDiscoveryClient implements DiscoveryClient, SmartInitializingSingleton, DisposableBean { +public final class UnionDiscoveryClient implements DiscoveryClient, ApplicationContextAware, SmartInitializingSingleton, DisposableBean { - private final ObjectProvider discoveryClientsProvider; + private ApplicationContext context; private List discoveryClients; - public UnionDiscoveryClient(ObjectProvider discoveryClientsProvider) { - this.discoveryClientsProvider = discoveryClientsProvider; - } - @Override public String description() { return "Union Discovery Client"; @@ -72,7 +72,7 @@ public List getServices() { List discoveryClients = getDiscoveryClients(); for (DiscoveryClient discoveryClient : discoveryClients) { List serviceForClient = discoveryClient.getServices(); - if (serviceForClient != null) { + if (isNotEmpty(serviceForClient)) { services.addAll(serviceForClient); } } @@ -85,9 +85,8 @@ public List getDiscoveryClients() { return discoveryClients; } - discoveryClients = new LinkedList<>(); - - for (DiscoveryClient discoveryClient : discoveryClientsProvider) { + discoveryClients = new ArrayList<>(); + for (DiscoveryClient discoveryClient : getSortedBeans(this.context, DiscoveryClient.class)) { String className = getClassName(discoveryClient.getClass()); if (COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME.equals(className) || this.equals(discoveryClient)) { // excludes CompositeDiscoveryClient and self @@ -95,6 +94,7 @@ public List getDiscoveryClients() { } discoveryClients.add(discoveryClient); } + this.discoveryClients = discoveryClients; return discoveryClients; } @@ -112,4 +112,9 @@ public void afterSingletonsInstantiated() { public void destroy() throws Exception { this.discoveryClients.clear(); } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = applicationContext; + } } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java index 101a6413..f1224270 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java @@ -18,7 +18,6 @@ import io.microsphere.annotation.ConfigurationProperty; import io.microsphere.spring.cloud.client.discovery.UnionDiscoveryClient; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -82,8 +81,8 @@ public class DiscoveryClientAutoConfiguration { public static class UnionConfiguration { @Bean - public UnionDiscoveryClient unionDiscoveryClient(ObjectProvider discoveryClientsProvider) { - return new UnionDiscoveryClient(discoveryClientsProvider); + public UnionDiscoveryClient unionDiscoveryClient() { + return new UnionDiscoveryClient(); } } } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientTest.java index 68b8bca2..60b49dc0 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration; @@ -29,10 +30,12 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; -import java.util.Arrays; import java.util.List; +import static io.microsphere.collection.Lists.ofList; +import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link UnionDiscoveryClient} Test @@ -46,10 +49,12 @@ SimpleDiscoveryClientAutoConfiguration.class, CompositeDiscoveryClientAutoConfiguration.class, UnionDiscoveryClient.class, + UnionDiscoveryClientTest.DummyDiscoveryClient.class, UnionDiscoveryClientTest.class }) @TestPropertySource( properties = { + "spring.cloud.discovery.client.simple.order=-1", "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", @@ -62,17 +67,70 @@ class UnionDiscoveryClientTest { @Autowired private DiscoveryClient discoveryClient; + @Autowired + private UnionDiscoveryClient unionDiscoveryClient; + @Test void test() { assertEquals(CompositeDiscoveryClient.class, discoveryClient.getClass()); CompositeDiscoveryClient compositeDiscoveryClient = CompositeDiscoveryClient.class.cast(discoveryClient); List discoveryClients = compositeDiscoveryClient.getDiscoveryClients(); - assertEquals(2, discoveryClients.size()); + assertEquals(3, discoveryClients.size()); assertEquals(UnionDiscoveryClient.class, discoveryClients.get(0).getClass()); assertEquals(SimpleDiscoveryClient.class, discoveryClients.get(1).getClass()); - List services = compositeDiscoveryClient.getServices(); - assertEquals(Arrays.asList("test"), services); - assertEquals(services, discoveryClients.get(0).getServices()); - assertEquals(services, discoveryClients.get(1).getServices()); + assertEquals(DummyDiscoveryClient.class, discoveryClients.get(2).getClass()); + } + + @Test + void testDescription() { + assertEquals("Composite Discovery Client", this.discoveryClient.description()); + assertEquals("Union Discovery Client", this.unionDiscoveryClient.description()); + } + + @Test + void testGetInstances() { + assertServiceInstances(this.discoveryClient.getInstances("test")); + assertServiceInstances(this.unionDiscoveryClient.getInstances("test")); + + assertTrue(this.discoveryClient.getInstances("unknown").isEmpty()); + assertTrue(this.unionDiscoveryClient.getInstances("unknown").isEmpty()); + } + + @Test + void testGetServices() { + assertServices(this.discoveryClient.getServices()); + assertServices(this.unionDiscoveryClient.getServices()); + } + + void assertServiceInstances(List serviceInstances) { + assertEquals(1, serviceInstances.size()); + ServiceInstance serviceInstance = serviceInstances.get(0); + assertEquals("test", serviceInstance.getServiceId()); + assertEquals("1", serviceInstance.getInstanceId()); + assertEquals("127.0.0.1", serviceInstance.getHost()); + assertEquals(8080, serviceInstance.getPort()); + assertEquals("value-1", serviceInstance.getMetadata().get("key-1")); + } + + void assertServices(List services) { + assertEquals(ofList("test"), services); + } + + static class DummyDiscoveryClient implements DiscoveryClient { + + @Override + public String description() { + return "Dummy Discovery Client"; + } + + @Override + public List getInstances(String serviceId) { + return emptyList(); + } + + @Override + public List getServices() { + return emptyList(); + } } -} +} \ No newline at end of file From 2e82509315d358cc811311fbfa87bdbc2c929fed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 20:04:36 +0800 Subject: [PATCH 126/310] Refactor null and empty check for service instances Replaced explicit null and empty check with isNotEmpty utility method for improved readability in UnionDiscoveryClient. --- .../spring/cloud/client/discovery/UnionDiscoveryClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java index f23f0987..bd2207a8 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java @@ -59,7 +59,7 @@ public List getInstances(String serviceId) { List discoveryClients = getDiscoveryClients(); for (DiscoveryClient discoveryClient : discoveryClients) { List instances = discoveryClient.getInstances(serviceId); - if (instances != null && !instances.isEmpty()) { + if (isNotEmpty(instances)) { serviceInstances.addAll(instances); } } From ea0bcf6cc2344e0e07d33e92d17f53704dec7008 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 20:05:34 +0800 Subject: [PATCH 127/310] Update UnionDiscoveryClient.java --- .../spring/cloud/client/discovery/UnionDiscoveryClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java index bd2207a8..944f0cdd 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java @@ -117,4 +117,4 @@ public void destroy() throws Exception { public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } -} +} \ No newline at end of file From ec6948d6a315403dec0a4a717dc7b3b5fc480dba Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 20:44:31 +0800 Subject: [PATCH 128/310] Refactor isRunning logic and add unit test Extracted the isRunning check into a static method in AbstractServiceRegistrationEndpoint for better testability and code reuse. Added a unit test to verify isRunning returns false when serviceRegistration is null. --- .../endpoint/AbstractServiceRegistrationEndpoint.java | 6 +++++- .../endpoint/AbstractServiceRegistrationEndpointTest.java | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java index c5e8eb6c..46a469cd 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java @@ -57,7 +57,11 @@ public void afterSingletonsInstantiated() { public void onApplicationEvent(WebServerInitializedEvent event) { WebServer webServer = event.getWebServer(); this.port = webServer.getPort(); - this.running = serviceRegistration == null ? false : serviceRegistration.isRunning(); + this.running = isRunning(serviceRegistration); + } + + static boolean isRunning(AbstractAutoServiceRegistration serviceRegistration) { + return serviceRegistration == null ? false : serviceRegistration.isRunning(); } protected boolean isRunning() { diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java index b1840e79..ee742e06 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java @@ -24,6 +24,7 @@ import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; import org.springframework.test.context.ContextConfiguration; +import static io.microsphere.spring.cloud.client.service.registry.endpoint.AbstractServiceRegistrationEndpoint.isRunning; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -51,4 +52,9 @@ void testOnApplicationEvent() { endpoint.onApplicationEvent(event); assertFalse(endpoint.isRunning()); } + + @Test + void testIsRunning() { + assertFalse(isRunning(null)); + } } \ No newline at end of file From a8f2a0bc11b730d5c312e07e69d931e909c295be Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 20:57:41 +0800 Subject: [PATCH 129/310] Make constructor public in SimpleAutoServiceRegistration Changed the constructor visibility from protected to public to allow instantiation of SimpleAutoServiceRegistration outside its package or subclasses. --- .../client/service/registry/SimpleAutoServiceRegistration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java index 30082573..1acc725f 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java @@ -34,7 +34,7 @@ public class SimpleAutoServiceRegistration extends AbstractAutoServiceRegistrati private final Registration registration; - protected SimpleAutoServiceRegistration(ServiceRegistry serviceRegistry, + public SimpleAutoServiceRegistration(ServiceRegistry serviceRegistry, AutoServiceRegistrationProperties properties, Registration registration) { super(serviceRegistry, properties); this.properties = properties; From d8fd03986fee5e02a54a7a4f5801e62cb3a2a79b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 20:57:45 +0800 Subject: [PATCH 130/310] Add tests for isRunning with SimpleAutoServiceRegistration Extended AbstractServiceRegistrationEndpointTest to verify isRunning behavior for SimpleAutoServiceRegistration before and after start. This improves test coverage for service registration endpoint logic. --- ...bstractServiceRegistrationEndpointTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java index ee742e06..3b9e11c1 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java @@ -17,15 +17,23 @@ package io.microsphere.spring.cloud.client.service.registry.endpoint; +import io.microsphere.spring.cloud.client.service.registry.InMemoryServiceRegistry; +import io.microsphere.spring.cloud.client.service.registry.SimpleAutoServiceRegistration; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.test.context.ContextConfiguration; +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static io.microsphere.spring.cloud.client.service.registry.endpoint.AbstractServiceRegistrationEndpoint.isRunning; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -56,5 +64,15 @@ void testOnApplicationEvent() { @Test void testIsRunning() { assertFalse(isRunning(null)); + ServiceRegistry serviceRegistry = new InMemoryServiceRegistry(); + AutoServiceRegistrationProperties properties = new AutoServiceRegistrationProperties(); + Registration registration = createDefaultRegistration(); + SimpleAutoServiceRegistration simpleAutoServiceRegistration = new SimpleAutoServiceRegistration(serviceRegistry, properties, registration); + GenericApplicationContext context = new GenericApplicationContext(); + context.refresh(); + simpleAutoServiceRegistration.setApplicationContext(context); + assertFalse(isRunning(simpleAutoServiceRegistration)); + simpleAutoServiceRegistration.start(); + assertTrue(isRunning(simpleAutoServiceRegistration)); } } \ No newline at end of file From 6ba11d70126523caf2fe56ea1c52d06f16ddd3e0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 21:17:02 +0800 Subject: [PATCH 131/310] Refactor LoadBalancerUtilsTest for clarity and speed Simplified the testCalculateWarmupWeight method by removing the loop and sleep calls. The test now directly asserts expected outcomes, improving test clarity and execution speed. --- .../tolerance/loadbalancer/util/LoadBalancerUtilsTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java index eaf3b721..c8a1ccf8 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java @@ -22,7 +22,6 @@ import static io.microsphere.spring.cloud.fault.tolerance.loadbalancer.util.LoadBalancerUtils.calculateWarmupWeight; import static java.lang.System.currentTimeMillis; -import static java.lang.Thread.sleep; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -38,9 +37,7 @@ class LoadBalancerUtilsTest { void testCalculateWarmupWeight() throws InterruptedException { long uptime = currentTimeMillis(); int weight = 10; - for (int i = 0; i < 10; i++) { - assertTrue(weight <= (weight = calculateWarmupWeight(uptime, uptime, weight))); - sleep(10); - } + assertTrue(calculateWarmupWeight(uptime, uptime, weight) >= 1); + assertTrue(calculateWarmupWeight(uptime, 1, weight) == 1); } } \ No newline at end of file From cfbd7bd5f1fc59092d9d245060dd517c6a4c07a0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 21:17:24 +0800 Subject: [PATCH 132/310] Update warmup weight assertion in test Changed the assertion in testCalculateWarmupWeight to require the calculated warmup weight to be strictly greater than 1 instead of greater than or equal to 1. --- .../tolerance/loadbalancer/util/LoadBalancerUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java index c8a1ccf8..8c32d4e8 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtilsTest.java @@ -37,7 +37,7 @@ class LoadBalancerUtilsTest { void testCalculateWarmupWeight() throws InterruptedException { long uptime = currentTimeMillis(); int weight = 10; - assertTrue(calculateWarmupWeight(uptime, uptime, weight) >= 1); + assertTrue(calculateWarmupWeight(uptime, uptime, weight) > 1); assertTrue(calculateWarmupWeight(uptime, 1, weight) == 1); } } \ No newline at end of file From 81784ab375f506c6c6575c5cf601e7f124b442f9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 21:45:10 +0800 Subject: [PATCH 133/310] Add microsphere-spring-boot-actuator dependency Added the microsphere-spring-boot-actuator module as a dependency in the commons POM to enable actuator features. --- microsphere-spring-cloud-commons/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index 287b0ba6..1a3a22d2 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -108,6 +108,11 @@ microsphere-spring-boot-core + + io.github.microsphere-projects + microsphere-spring-boot-actuator + + io.github.microsphere-projects microsphere-spring-webmvc From 694d97a43be3ec027a337681dab1592fd03129ac Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Wed, 22 Oct 2025 21:45:16 +0800 Subject: [PATCH 134/310] Improve endpoint availability checks in test Updated ServiceRegistrationEndpointAutoConfigurationTest to use getIfAvailable() for endpoint beans and added a check for serviceDeregistrationEndpoint. Also simplified the webEnvironment assignment in the @SpringBootTest annotation. --- .../ServiceRegistrationEndpointAutoConfigurationTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java index 9e87e0cc..6bc1a536 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfigurationTest.java @@ -9,6 +9,7 @@ import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * {@link ServiceRegistrationEndpointAutoConfiguration} Test @@ -21,7 +22,7 @@ classes = { ServiceRegistrationEndpointAutoConfigurationTest.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + webEnvironment = RANDOM_PORT, properties = { "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", "management.endpoint.serviceRegistration.enabled=true", @@ -39,7 +40,7 @@ class ServiceRegistrationEndpointAutoConfigurationTest { @Test void testEndpoints() { - assertNotNull(serviceRegistrationEndpoint); + assertNotNull(serviceRegistrationEndpoint.getIfAvailable()); + assertNotNull(serviceDeregistrationEndpoint.getIfAvailable()); } - } \ No newline at end of file From 665ef6a94e098a6b61199ac6222ce9622a9b3c90 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 10:05:00 +0800 Subject: [PATCH 135/310] Add microsphere-spring-test as test dependency Added the microsphere-spring-test library to the test dependencies in pom.xml to support additional testing capabilities. --- microsphere-spring-cloud-commons/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index 1a3a22d2..e68c2819 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -132,6 +132,13 @@ test + + + io.github.microsphere-projects + microsphere-spring-test + test + + org.testcontainers From ef8cb571866935366dc0caf38b284964f6aa7a2d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 10:05:08 +0800 Subject: [PATCH 136/310] Refactor ServiceInstanceUtils for symbol constants Replaced hardcoded JSON symbols in ServiceInstanceUtils with constants from SymbolConstants for improved maintainability. Also removed line separators from JSON before encoding and adjusted index calculation for kind insertion in JSON. --- .../client/service/util/ServiceInstanceUtils.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index ebbc4f2e..9f28f3c2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -36,7 +36,9 @@ import static io.microsphere.constants.SymbolConstants.COLON; import static io.microsphere.constants.SymbolConstants.COMMA; import static io.microsphere.constants.SymbolConstants.DOUBLE_QUOTE; +import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET; import static io.microsphere.constants.SymbolConstants.RIGHT_CURLY_BRACE; +import static io.microsphere.constants.SymbolConstants.RIGHT_SQUARE_BRACKET; import static io.microsphere.json.JSONUtils.jsonArray; import static io.microsphere.json.JSONUtils.readArray; import static io.microsphere.logging.LoggerFactory.getLogger; @@ -46,6 +48,7 @@ import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_MAPPINGS_METADATA_NAME; import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.valueOf; import static io.microsphere.spring.web.metadata.WebEndpointMapping.of; +import static io.microsphere.util.StringUtils.EMPTY_STRING; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; import static io.microsphere.util.StringUtils.isBlank; import static java.util.Collections.emptyList; @@ -62,11 +65,13 @@ public class ServiceInstanceUtils extends BaseUtils { public static void attachMetadata(String contextPath, ServiceInstance serviceInstance, Collection webEndpointMappings) { Map metadata = serviceInstance.getMetadata(); - StringJoiner jsonBuilder = new StringJoiner(",", "[", "]"); + StringJoiner jsonBuilder = new StringJoiner(COMMA, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET); webEndpointMappings.stream().map(mapping -> toJSON(mapping)).forEach(jsonBuilder::add); String json = jsonBuilder.toString(); - metadata.put(WEB_CONTEXT_PATH_METADATA_NAME, contextPath); + json = json.replace(LINE_SEPARATOR, EMPTY_STRING); String encodedJson = encode(json); + + metadata.put(WEB_CONTEXT_PATH_METADATA_NAME, contextPath); metadata.put(WEB_MAPPINGS_METADATA_NAME, encodedJson); } @@ -88,7 +93,7 @@ static String toJSON(WebEndpointMapping webEndpointMapping) { String json = webEndpointMapping.toJSON(); StringBuilder jsonBuilder = new StringBuilder(json); int startIndex = jsonBuilder.lastIndexOf(LINE_SEPARATOR); - int endIndex = jsonBuilder.indexOf(RIGHT_CURLY_BRACE); + int endIndex = jsonBuilder.indexOf(RIGHT_CURLY_BRACE, startIndex); String kindItem = COMMA + LINE_SEPARATOR + DOUBLE_QUOTE + "kind" + DOUBLE_QUOTE + COLON + DOUBLE_QUOTE + webEndpointMapping.getKind() + DOUBLE_QUOTE + LINE_SEPARATOR; jsonBuilder.replace(startIndex, endIndex, kindItem); @@ -152,7 +157,7 @@ public static String getMetadata(ServiceInstance serviceInstance, String metadat Map metadata = serviceInstance.getMetadata(); return metadata.get(metadataName); } - + private ServiceInstanceUtils() { } } From 21e4e534acab533512e5dc521dcbb1d5284f97d5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 10:05:13 +0800 Subject: [PATCH 137/310] Update test config to use TestController class Changed the @SpringBootTest 'classes' attribute in WebMvcServiceRegistryAutoConfigurationTest to use TestController instead of the test class itself. This ensures the test context loads the correct controller for testing. --- .../WebMvcServiceRegistryAutoConfigurationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java index f556848d..cf25e583 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java @@ -16,6 +16,7 @@ */ package io.microsphere.spring.cloud.client.service.registry.autoconfigure; +import io.microsphere.spring.test.web.controller.TestController; import io.microsphere.spring.webmvc.annotation.EnableWebMvcExtension; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -36,7 +37,7 @@ * @since 1.0.0 */ @SpringBootTest( - classes = WebMvcServiceRegistryAutoConfigurationTest.class, + classes = TestController.class, properties = { "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", "spring.cloud.service-registry.auto-registration.enabled=true", From bf7271a1e3fc6bbee9bb9ec295d012042a5891a1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 10:36:42 +0800 Subject: [PATCH 138/310] Add trace logging for web endpoint mappings metadata Added detailed trace-level logging to output the JSON representation of web endpoint mappings and the resulting ServiceInstance metadata. This helps with debugging and visibility into the metadata attachment process. --- .../cloud/client/service/util/ServiceInstanceUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 9f28f3c2..e12b6016 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -65,14 +65,17 @@ public class ServiceInstanceUtils extends BaseUtils { public static void attachMetadata(String contextPath, ServiceInstance serviceInstance, Collection webEndpointMappings) { Map metadata = serviceInstance.getMetadata(); - StringJoiner jsonBuilder = new StringJoiner(COMMA, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET); + StringJoiner jsonBuilder = new StringJoiner(COMMA + LINE_SEPARATOR, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET); webEndpointMappings.stream().map(mapping -> toJSON(mapping)).forEach(jsonBuilder::add); String json = jsonBuilder.toString(); + logger.trace("Web Endpoint Mappings JSON: \n{}", json); json = json.replace(LINE_SEPARATOR, EMPTY_STRING); String encodedJson = encode(json); metadata.put(WEB_CONTEXT_PATH_METADATA_NAME, contextPath); metadata.put(WEB_MAPPINGS_METADATA_NAME, encodedJson); + logger.trace("ServiceInstance's metadata :"); + metadata.forEach((name, value) -> logger.trace("{} : {}", name, value)); } /** From 3e397e20c11ac50f852abd1aa6c376b53175c7b2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 11:51:29 +0800 Subject: [PATCH 139/310] Add WebServiceRegistryAutoConfiguration class Introduces an abstract auto-configuration class for ServiceRegistry integration in Spring WebMVC applications. Handles attaching web endpoint metadata to service registration, with exclusion logic for actuator and custom mappings. --- .../WebServiceRegistryAutoConfiguration.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java new file mode 100644 index 00000000..8145a468 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.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 io.microsphere.spring.cloud.client.service.registry.autoconfigure; + +import io.microsphere.logging.Logger; +import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; +import io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent; +import io.microsphere.spring.web.metadata.WebEndpointMapping; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; + +/** + * Auto-Configuration class for {@link ServiceRegistry ServiceRegistry} on the Spring WebMVC Application + * + * @author Mercy + * @since 1.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(name = { + "io.microsphere.spring.web.metadata.WebEndpointMapping", + "io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent" +}) +@ConditionalOnBean(Registration.class) +@ConditionalOnAutoServiceRegistrationEnabled +@AutoConfigureAfter(value = { + ServiceRegistryAutoConfiguration.class +}) +public abstract class WebServiceRegistryAutoConfiguration implements ApplicationListener { + + protected final Logger logger = getLogger(getClass()); + + @Autowired + protected Registration registration; + + @Value("${management.endpoints.web.base-path:/actuator}") + protected String actuatorBasePath; + + @Override + public final void onApplicationEvent(WebEndpointMappingsReadyEvent event) { + Collection webEndpointMappings = event.getMappings(); + attachWebMappingsMetadata(registration, webEndpointMappings); + } + + private void attachWebMappingsMetadata(Registration registration, Collection webEndpointMappings) { + Set mappings = new HashSet<>(webEndpointMappings); + excludeMappings(mappings); + attachMetadata(getContextPath(), registration, mappings); + } + + private void excludeMappings(Set mappings) { + Iterator iterator = mappings.iterator(); + while (iterator.hasNext()) { + WebEndpointMapping mapping = iterator.next(); + String[] patterns = mapping.getPatterns(); + if (isExcludedMapping(mapping, patterns) || isActuatorWebEndpointMapping(mapping, patterns)) { + logger.trace("The '{}' was excluded", mapping); + iterator.remove(); + } + } + } + + /** + * Get the context path of the Spring Web Application + * + * @return context path + */ + protected abstract String getContextPath(); + + /** + * Is excluded mapping + * + * @param mapping {@link WebEndpointMapping} + * @param patterns patterns + * @return if excluded mapping, return true, or false + */ + protected abstract boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns); + + /** + * Is actuator {@link WebEndpointMapping} + * + * @param mapping {@link WebEndpointMapping} + * @param patterns patterns + * @return if actuator {@link WebEndpointMapping}, return true, or false + */ + protected boolean isActuatorWebEndpointMapping(WebEndpointMapping mapping, String[] patterns) { + for (String pattern : patterns) { + if (pattern.startsWith(actuatorBasePath)) { + return true; + } + } + return false; + } +} \ No newline at end of file From 23a216bf7a84671d97ffdca1fe6f06f6227707c4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 11:51:49 +0800 Subject: [PATCH 140/310] Refactor WebMvcServiceRegistryAutoConfiguration class Simplified the class by removing unused imports, annotations, and methods. Refactored exclusion logic for endpoint mappings and moved shared logic to the superclass WebServiceRegistryAutoConfiguration for better maintainability. --- ...ebMvcServiceRegistryAutoConfiguration.java | 77 ++----------------- 1 file changed, 8 insertions(+), 69 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index d838c25c..1a8fc0d7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -16,34 +16,20 @@ */ package io.microsphere.spring.cloud.client.service.registry.autoconfigure; -import io.microsphere.logging.Logger; -import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; -import io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent; import io.microsphere.spring.web.metadata.WebEndpointMapping; import io.microsphere.util.ValueHolder; import jakarta.servlet.Filter; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.event.EventListener; import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; import java.util.Objects; -import java.util.Set; -import static io.microsphere.logging.LoggerFactory.getLogger; -import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; import static io.microsphere.util.ArrayUtils.EMPTY_STRING_ARRAY; import static io.microsphere.util.ArrayUtils.arrayEquals; import static java.lang.Boolean.FALSE; @@ -55,29 +41,11 @@ * @author Mercy * @since 1.0.0 */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(name = { - "io.microsphere.spring.web.metadata.WebEndpointMapping", - "io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent" -}) -@ConditionalOnBean(Registration.class) @ConditionalOnWebApplication(type = SERVLET) -@ConditionalOnAutoServiceRegistrationEnabled -@AutoConfigureAfter(value = { - ServiceRegistryAutoConfiguration.class -}) -public class WebMvcServiceRegistryAutoConfiguration { - - private static final Logger logger = getLogger(WebMvcServiceRegistryAutoConfiguration.class); +public class WebMvcServiceRegistryAutoConfiguration extends WebServiceRegistryAutoConfiguration { private static final String[] DEFAULT_URL_MAPPINGS = {"/*"}; - @Autowired - private Registration registration; - - @Value("${management.endpoints.web.base-path:/actuator}") - private String actuatorBasePath; - @Value("${server.servlet.context-path:}") private String contextPath; @@ -87,32 +55,14 @@ public class WebMvcServiceRegistryAutoConfiguration { @Autowired private ObjectProvider dispatcherServletRegistrationBeanProvider; - @EventListener(WebEndpointMappingsReadyEvent.class) - public void onApplicationEvent(WebEndpointMappingsReadyEvent event) { - Collection webEndpointMappings = event.getMappings(); - attachWebMappingsMetadata(registration, webEndpointMappings); + @Override + protected String getContextPath() { + return this.contextPath; } - private void attachWebMappingsMetadata(Registration registration, Collection webEndpointMappings) { - Set mappings = new HashSet<>(webEndpointMappings); - excludeMappings(mappings); - attachMetadata(contextPath, registration, mappings); - } - - private void excludeMappings(Set mappings) { - Iterator iterator = mappings.iterator(); - while (iterator.hasNext()) { - WebEndpointMapping mapping = iterator.next(); - String[] patterns = mapping.getPatterns(); - if (isBuiltInFilterMapping(patterns) - || isDispatcherServletMapping(mapping, patterns) - || isActuatorWebEndpointMapping(patterns) - ) { - logger.trace("The '{}' was removed", mapping); - iterator.remove(); - } - - } + @Override + protected boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns) { + return isBuiltInFilterMapping(patterns) || isDispatcherServletMapping(mapping, patterns); } private boolean isBuiltInFilterMapping(String[] patterns) { @@ -143,16 +93,6 @@ private boolean isDispatcherServletMapping(WebEndpointMapping mapping, String[] return found.getValue(); } - - private boolean isActuatorWebEndpointMapping(String[] patterns) { - for (String pattern : patterns) { - if (pattern.startsWith(actuatorBasePath)) { - return true; - } - } - return false; - } - private boolean matchFilter(FilterRegistrationBean filterRegistrationBean, String[] patterns) { Collection urlPatterns = filterRegistrationBean.getUrlPatterns(); return matchUrlPatterns(urlPatterns, patterns); @@ -162,5 +102,4 @@ private boolean matchUrlPatterns(Collection urlPatterns, String[] patter String[] urlPatternsArray = urlPatterns.isEmpty() ? DEFAULT_URL_MAPPINGS : urlPatterns.toArray(EMPTY_STRING_ARRAY); return arrayEquals(urlPatternsArray, patterns); } - -} +} \ No newline at end of file From 4c79cc33cec1af115c1ffa267257d4c556292ee3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 12:43:40 +0800 Subject: [PATCH 141/310] Refactor WebFlux auto-config and add shared tests Refactored WebFluxServiceRegistryAutoConfiguration to extend WebServiceRegistryAutoConfiguration, simplifying context path and mapping exclusion logic. Added WebServiceRegistryAutoConfigurationTest as a shared abstract test, and updated WebMvcServiceRegistryAutoConfigurationTest and introduced WebFluxServiceRegistryAutoConfigurationTest to use it for consistent testing of service registry metadata. --- ...bFluxServiceRegistryAutoConfiguration.java | 26 ++++--- ...xServiceRegistryAutoConfigurationTest.java | 40 +++++++++++ ...cServiceRegistryAutoConfigurationTest.java | 36 +--------- ...bServiceRegistryAutoConfigurationTest.java | 68 +++++++++++++++++++ 4 files changed, 122 insertions(+), 48 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfigurationTest.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java index 0cb30de1..cafa1eb5 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java @@ -16,12 +16,9 @@ */ package io.microsphere.spring.cloud.client.service.registry.autoconfigure; -import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import io.microsphere.spring.web.metadata.WebEndpointMapping; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.context.annotation.Configuration; import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.REACTIVE; @@ -31,15 +28,16 @@ * @author Mercy * @since 1.0.0 */ -@Configuration(proxyBeanMethods = false) -@ConditionalOnClass(name = { - "io.microsphere.spring.web.metadata.WebEndpointMapping", - "io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent" -}) @ConditionalOnWebApplication(type = REACTIVE) -@ConditionalOnAutoServiceRegistrationEnabled -@AutoConfigureAfter(value = { - ServiceRegistryAutoConfiguration.class -}) -public class WebFluxServiceRegistryAutoConfiguration { +public class WebFluxServiceRegistryAutoConfiguration extends WebServiceRegistryAutoConfiguration { + + @Override + protected String getContextPath() { + return ""; + } + + @Override + protected boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns) { + return false; + } } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfigurationTest.java new file mode 100644 index 00000000..a3fdb1e5 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfigurationTest.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry.autoconfigure; + + +import io.microsphere.spring.webflux.annotation.EnableWebFluxExtension; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.test.context.TestPropertySource; + +/** + * {@link WebFluxServiceRegistryAutoConfiguration} Test + * + * @author Mercy + * @see WebFluxServiceRegistryAutoConfiguration + * @since 1.0.0 + */ +@TestPropertySource( + properties = { + "spring.main.web-application-type=reactive" + } +) +@EnableWebFluxExtension +@EnableAutoConfiguration +class WebFluxServiceRegistryAutoConfigurationTest extends WebServiceRegistryAutoConfigurationTest { +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java index cf25e583..09730c57 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java @@ -16,19 +16,8 @@ */ package io.microsphere.spring.cloud.client.service.registry.autoconfigure; -import io.microsphere.spring.test.web.controller.TestController; import io.microsphere.spring.webmvc.annotation.EnableWebMvcExtension; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.client.serviceregistry.Registration; - -import java.util.Map; - -import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_MAPPINGS_METADATA_NAME; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * {@link WebMvcServiceRegistryAutoConfiguration} Test @@ -36,28 +25,7 @@ * @author Mercy * @since 1.0.0 */ -@SpringBootTest( - classes = TestController.class, - properties = { - "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", - "spring.cloud.service-registry.auto-registration.enabled=true", - "spring.cloud.kubernetes.enabled=false", - "kubernetes.informer.enabled=false", - "kubernetes.manifests.enabled=false", - "kubernetes.reconciler.enabled=false" - }, - webEnvironment = RANDOM_PORT -) -@EnableAutoConfiguration @EnableWebMvcExtension -class WebMvcServiceRegistryAutoConfigurationTest { - - @Autowired - private Registration registration; - - @Test - void test() { - Map metadata = registration.getMetadata(); - assertNotNull(metadata.get(WEB_MAPPINGS_METADATA_NAME)); - } +@EnableAutoConfiguration +class WebMvcServiceRegistryAutoConfigurationTest extends WebServiceRegistryAutoConfigurationTest { } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java new file mode 100644 index 00000000..e631368e --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry.autoconfigure; + + +import io.microsphere.spring.test.web.controller.TestController; +import io.microsphere.spring.web.metadata.WebEndpointMapping; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.serviceregistry.Registration; + +import java.util.Collection; +import java.util.Map; + +import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; +import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_MAPPINGS_METADATA_NAME; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * {@link WebServiceRegistryAutoConfiguration} Test + * + * @author Mercy + * @see WebServiceRegistryAutoConfiguration + * @since 1.0.0 + */ +@SpringBootTest( + classes = TestController.class, + properties = { + "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", + "spring.cloud.service-registry.auto-registration.enabled=true" + }, + webEnvironment = RANDOM_PORT +) +abstract class WebServiceRegistryAutoConfigurationTest { + + @Autowired + private Registration registration; + + @Test + void test() { + Map metadata = registration.getMetadata(); + assertEquals("", metadata.get(WEB_CONTEXT_PATH_METADATA_NAME)); + assertNotNull(metadata.get(WEB_MAPPINGS_METADATA_NAME)); + + Collection webEndpointMappings = getWebEndpointMappings(registration); + assertTrue(webEndpointMappings.size() >= 6); + } +} \ No newline at end of file From 2cc58f6372dc0e864236d56525857c01959e39d6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 13:53:19 +0800 Subject: [PATCH 142/310] Refactor registration bean retrieval in auto-configuration Replaces field injection of Registration with runtime lookup using getOptionalBean from the ApplicationContext during WebEndpointMappingsReadyEvent handling. This improves flexibility and avoids potential issues with bean availability. --- .../WebServiceRegistryAutoConfiguration.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java index 8145a468..1d01709a 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java @@ -20,13 +20,13 @@ import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; import io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent; import io.microsphere.spring.web.metadata.WebEndpointMapping; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Configuration; @@ -36,6 +36,7 @@ import java.util.Set; import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.spring.beans.BeanUtils.getOptionalBean; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; /** @@ -58,16 +59,17 @@ public abstract class WebServiceRegistryAutoConfiguration implements Application protected final Logger logger = getLogger(getClass()); - @Autowired - protected Registration registration; - @Value("${management.endpoints.web.base-path:/actuator}") protected String actuatorBasePath; @Override public final void onApplicationEvent(WebEndpointMappingsReadyEvent event) { - Collection webEndpointMappings = event.getMappings(); - attachWebMappingsMetadata(registration, webEndpointMappings); + ApplicationContext context = event.getApplicationContext(); + Registration registration = getOptionalBean(context, Registration.class); + if (registration != null) { + Collection webEndpointMappings = event.getMappings(); + attachWebMappingsMetadata(registration, webEndpointMappings); + } } private void attachWebMappingsMetadata(Registration registration, Collection webEndpointMappings) { From 43ef84b66d1064ecceed8d4f8fff77aa05760207 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 13:53:26 +0800 Subject: [PATCH 143/310] Refactor SpringBootTest webEnvironment usage Replaces explicit enum reference with static import for RANDOM_PORT in SimpleAutoServiceRegistrationAutoConfigurationTest. This improves code readability and consistency. --- .../SimpleAutoServiceRegistrationAutoConfigurationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java index 71d01bad..8fa8f2e0 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfigurationTest.java @@ -10,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * {@link SimpleAutoServiceRegistrationAutoConfiguration} Test @@ -22,7 +23,7 @@ classes = { SimpleAutoServiceRegistrationAutoConfigurationTest.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + webEnvironment = RANDOM_PORT, properties = { "microsphere.spring.cloud.service-registry.auto-registration.simple.enabled=true", "spring.application.name=test-service" From abc4d05d0d7f0ec44c546b328c707a6433226601 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 13:53:31 +0800 Subject: [PATCH 144/310] Refactor SpringBootTest annotation usage in test Simplified the @SpringBootTest annotation in TomcatFaultToleranceAutoConfigurationTest by using the RANDOM_PORT constant directly and removing redundant array syntax for classes. --- .../TomcatFaultToleranceAutoConfigurationTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java index 189f2013..a5ab57d9 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfigurationTest.java @@ -34,6 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * {@link TomcatFaultToleranceAutoConfiguration} Test @@ -41,9 +42,7 @@ * @author Mercy * @since 1.0.0 */ -@SpringBootTest(classes = { - TomcatFaultToleranceAutoConfigurationTest.class}, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(classes = TomcatFaultToleranceAutoConfigurationTest.class, webEnvironment = RANDOM_PORT) @EnableAutoConfiguration class TomcatFaultToleranceAutoConfigurationTest { From b089f291175ecdd9fb1bdcf37ffe022436f31dba Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 13:53:37 +0800 Subject: [PATCH 145/310] Refactor SpringBootTest webEnvironment usage Replaced explicit enum reference with static import for webEnvironment in RequestInterceptorChangedTest. This improves code readability and consistency. --- .../requestInterceptor/RequestInterceptorChangedTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java index bda755f6..09e2f835 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/requestInterceptor/RequestInterceptorChangedTest.java @@ -6,11 +6,13 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE; + /** * @author 韩超 * @since 0.0.1 */ -@SpringBootTest(classes = RequestInterceptorChangedTest.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +@SpringBootTest(classes = RequestInterceptorChangedTest.class, webEnvironment = NONE) @EnableAutoConfiguration class RequestInterceptorChangedTest extends BaseTest { From c033671c4ea3785ce5ccc4b6f1f2ff84e1754873 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 13:56:11 +0800 Subject: [PATCH 146/310] Refactor test class to use package-private visibility Changed ServiceRegistrationEndpointTest from public to package-private to align with JUnit 5 best practices and reduce unnecessary class exposure. --- .../registry/endpoint/ServiceRegistrationEndpointTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java index 8f5b3eb9..fba4e41f 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java @@ -44,7 +44,7 @@ ServiceRegistrationEndpoint.class }) @EnableAutoConfiguration -public class ServiceRegistrationEndpointTest extends BaseServiceRegistrationEndpointTest { +class ServiceRegistrationEndpointTest extends BaseServiceRegistrationEndpointTest { @Autowired private ServiceRegistrationEndpoint endpoint; From e6cb6b40be1348af1090d6b3f72a06fd68947425 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 14:18:08 +0800 Subject: [PATCH 147/310] Refactor registration bean retrieval in auto config Replaced usage of getOptionalBean with ObjectProvider for Registration bean retrieval in WebServiceRegistryAutoConfiguration. This allows for cleaner iteration over available Registration beans and simplifies the event handling logic. --- .../WebServiceRegistryAutoConfiguration.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java index 1d01709a..e025a791 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java @@ -20,6 +20,7 @@ import io.microsphere.spring.cloud.client.service.registry.condition.ConditionalOnAutoServiceRegistrationEnabled; import io.microsphere.spring.web.event.WebEndpointMappingsReadyEvent; import io.microsphere.spring.web.metadata.WebEndpointMapping; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -36,7 +37,6 @@ import java.util.Set; import static io.microsphere.logging.LoggerFactory.getLogger; -import static io.microsphere.spring.beans.BeanUtils.getOptionalBean; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; /** @@ -65,11 +65,9 @@ public abstract class WebServiceRegistryAutoConfiguration implements Application @Override public final void onApplicationEvent(WebEndpointMappingsReadyEvent event) { ApplicationContext context = event.getApplicationContext(); - Registration registration = getOptionalBean(context, Registration.class); - if (registration != null) { - Collection webEndpointMappings = event.getMappings(); - attachWebMappingsMetadata(registration, webEndpointMappings); - } + ObjectProvider registrationProvider = context.getBeanProvider(Registration.class); + Collection webEndpointMappings = event.getMappings(); + registrationProvider.forEach(registration -> attachWebMappingsMetadata(registration, webEndpointMappings)); } private void attachWebMappingsMetadata(Registration registration, Collection webEndpointMappings) { From 1ac5563f652a1760d4a4c4f91ab3711e68f25940 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 14:33:04 +0800 Subject: [PATCH 148/310] Update AbstractServiceRegistrationEndpoint.java --- .../endpoint/AbstractServiceRegistrationEndpoint.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java index 46a469cd..7e2dae49 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java @@ -57,10 +57,10 @@ public void afterSingletonsInstantiated() { public void onApplicationEvent(WebServerInitializedEvent event) { WebServer webServer = event.getWebServer(); this.port = webServer.getPort(); - this.running = isRunning(serviceRegistration); + this.running = detectRunning(serviceRegistration); } - static boolean isRunning(AbstractAutoServiceRegistration serviceRegistration) { + static boolean detectRunning(AbstractAutoServiceRegistration serviceRegistration) { return serviceRegistration == null ? false : serviceRegistration.isRunning(); } From 8e2e35dc6cc42e99520af2310f5d091ade6d7930 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 14:33:06 +0800 Subject: [PATCH 149/310] Update AbstractServiceRegistrationEndpointTest.java --- .../AbstractServiceRegistrationEndpointTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java index 3b9e11c1..e0264f87 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpointTest.java @@ -31,7 +31,7 @@ import org.springframework.test.context.ContextConfiguration; import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; -import static io.microsphere.spring.cloud.client.service.registry.endpoint.AbstractServiceRegistrationEndpoint.isRunning; +import static io.microsphere.spring.cloud.client.service.registry.endpoint.AbstractServiceRegistrationEndpoint.detectRunning; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -62,8 +62,8 @@ void testOnApplicationEvent() { } @Test - void testIsRunning() { - assertFalse(isRunning(null)); + void testDetectRunning() { + assertFalse(detectRunning(null)); ServiceRegistry serviceRegistry = new InMemoryServiceRegistry(); AutoServiceRegistrationProperties properties = new AutoServiceRegistrationProperties(); Registration registration = createDefaultRegistration(); @@ -71,8 +71,8 @@ void testIsRunning() { GenericApplicationContext context = new GenericApplicationContext(); context.refresh(); simpleAutoServiceRegistration.setApplicationContext(context); - assertFalse(isRunning(simpleAutoServiceRegistration)); + assertFalse(detectRunning(simpleAutoServiceRegistration)); simpleAutoServiceRegistration.start(); - assertTrue(isRunning(simpleAutoServiceRegistration)); + assertTrue(detectRunning(simpleAutoServiceRegistration)); } } \ No newline at end of file From 6013054185b7acee8b40d21aafe4852a2a002e47 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 23 Oct 2025 14:33:08 +0800 Subject: [PATCH 150/310] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a6038238..b6a2b380 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.microsphere-projects microsphere-build - 0.2.1 + 0.2.2 4.0.0 From d2bea3efeafb835d3632b16f2f20beedca1212dc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 11:25:27 +0800 Subject: [PATCH 151/310] Refactor Feign component type resolution logic Simplified the componentType() methods in DecoratedContract, DecoratedDecoder, DecoratedEncoder, DecoratedErrorDecoder, and DecoratedRetryer by introducing a generic get() helper in DecoratedFeignComponent. This reduces code duplication and improves maintainability by centralizing configuration value retrieval. --- .../components/DecoratedContract.java | 14 ++++--------- .../components/DecoratedDecoder.java | 13 +++--------- .../components/DecoratedEncoder.java | 13 +++--------- .../components/DecoratedErrorDecoder.java | 13 +++--------- .../components/DecoratedFeignComponent.java | 21 +++++++++++++++++-- .../components/DecoratedRetryer.java | 15 ++++--------- 6 files changed, 36 insertions(+), 53 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index 025ef5f2..6917348c 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -4,6 +4,7 @@ import feign.MethodMetadata; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; import java.util.List; @@ -20,18 +21,11 @@ public DecoratedContract(String contextId, NamedContextFactory componentType() { - Class contractClass = null; - if (getDefaultConfiguration() != null && getDefaultConfiguration().getContract() != null) - contractClass = getDefaultConfiguration().getContract(); - - if (getCurrentConfiguration() != null && getCurrentConfiguration().getContract() != null) - contractClass = getCurrentConfiguration().getContract(); - - if (contractClass != null) - return contractClass; - return Contract.class; + Class contractClass = get(FeignClientConfiguration::getContract); + return contractClass == null ? Contract.class : contractClass; } + @Override public List parseAndValidateMetadata(Class targetType) { return delegate().parseAndValidateMetadata(targetType); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index afcb0aee..6d230df0 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -6,6 +6,7 @@ import feign.codec.Decoder; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; import java.io.IOException; @@ -23,16 +24,8 @@ public DecoratedDecoder(String contextId, NamedContextFactory componentType() { - Class decoderClass = null; - if (getDefaultConfiguration() != null && getDefaultConfiguration().getDecoder() != null) - decoderClass = getDefaultConfiguration().getDecoder(); - - if (getCurrentConfiguration() != null && getCurrentConfiguration().getDecoder() != null) - decoderClass = getCurrentConfiguration().getDecoder(); - - if (decoderClass != null) - return decoderClass; - return Decoder.class; + Class decoderClass = get(FeignClientConfiguration::getDecoder); + return decoderClass == null ? Decoder.class : decoderClass; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index 951e6ca3..e712bfeb 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -5,6 +5,7 @@ import feign.codec.Encoder; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; import java.lang.reflect.Type; @@ -21,16 +22,8 @@ public DecoratedEncoder(String contextId, NamedContextFactory componentType() { - Class encoderClass = null; - if (getDefaultConfiguration() != null && getDefaultConfiguration().getEncoder() != null) - encoderClass = getDefaultConfiguration().getEncoder(); - - if (getCurrentConfiguration() != null && getCurrentConfiguration().getEncoder() != null) - encoderClass = getCurrentConfiguration().getEncoder(); - - if (encoderClass != null) - return encoderClass; - return Encoder.class; + Class encoderClass = get(FeignClientConfiguration::getEncoder); + return encoderClass == null ? Encoder.class : encoderClass; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index c9c97d7a..530bf81d 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -4,6 +4,7 @@ import feign.codec.ErrorDecoder; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; /** @@ -18,16 +19,8 @@ public DecoratedErrorDecoder(String contextId, NamedContextFactory componentType() { - Class errorDecoderClass = null; - if (getDefaultConfiguration() != null && getDefaultConfiguration().getErrorDecoder() != null) - errorDecoderClass = getDefaultConfiguration().getErrorDecoder(); - - if (getCurrentConfiguration() != null && getCurrentConfiguration().getErrorDecoder() != null) - errorDecoderClass = getCurrentConfiguration().getErrorDecoder(); - - if (errorDecoderClass != null) - return errorDecoderClass; - return ErrorDecoder.class; + Class errorDecoderClass = get(FeignClientConfiguration::getErrorDecoder); + return errorDecoderClass == null ? ErrorDecoder.class : errorDecoderClass; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 0995f2ca..4959abbe 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -6,11 +6,13 @@ import org.springframework.beans.BeanUtils; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; import org.springframework.lang.NonNull; import java.lang.reflect.Constructor; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; /** * @author 韩超 @@ -75,14 +77,29 @@ public void refresh() { protected abstract Class componentType(); - public FeignClientProperties.FeignClientConfiguration getDefaultConfiguration() { + public FeignClientConfiguration getDefaultConfiguration() { return this.clientProperties.getConfig().get(this.clientProperties.getDefaultConfig()); } - public FeignClientProperties.FeignClientConfiguration getCurrentConfiguration() { + public FeignClientConfiguration getCurrentConfiguration() { return this.clientProperties.getConfig().get(contextId); } + protected T get(Function configurationFunction) { + FeignClientConfiguration config = getDefaultConfiguration(); + T value = null; + if (config != null) { + value = configurationFunction.apply(config); + } + if (value == null) { + config = getCurrentConfiguration(); + if (config != null) { + value = configurationFunction.apply(config); + } + } + return value; + } + protected T loadInstance() { Class componentType = componentType(); String contextId = contextId(); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java index 3d1cb2c3..32441dea 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java @@ -4,13 +4,14 @@ import feign.Retryer; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; /** * @author 韩超 * @since 0.0.1 */ -public class DecoratedRetryer extends DecoratedFeignComponent implements Retryer { +public class DecoratedRetryer extends DecoratedFeignComponent implements Retryer { public DecoratedRetryer(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Retryer delegate) { super(contextId, contextFactory, clientProperties, delegate); @@ -18,16 +19,8 @@ public DecoratedRetryer(String contextId, NamedContextFactory componentType() { - Class retryerClass = null; - if (getDefaultConfiguration() != null && getDefaultConfiguration().getRetryer() != null) - retryerClass = getDefaultConfiguration().getRetryer(); - - if (getCurrentConfiguration() != null && getCurrentConfiguration().getRetryer() != null) - retryerClass = getCurrentConfiguration().getRetryer(); - - if (retryerClass != null) - return retryerClass; - return Retryer.class; + Class retryerClass = get(FeignClientConfiguration::getRetryer); + return retryerClass == null ? Retryer.class : retryerClass; } @Override From a2b83e9d8749ff92f02ad4ee4ed744877441859b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 13:44:57 +0800 Subject: [PATCH 152/310] Update dependency comment in pom.xml Changed the comment for the microsphere-spring-test dependency from 'Microsphere' to 'Microsphere Test' for improved clarity. --- microsphere-spring-cloud-commons/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index e68c2819..f9869aca 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -132,7 +132,7 @@ test - + io.github.microsphere-projects microsphere-spring-test From c935ecffde45dc06d203a3676237deac46dab637 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 13:45:03 +0800 Subject: [PATCH 153/310] Add microsphere-spring-test to test dependencies Added the microsphere-spring-test library as a test dependency in pom.xml to support additional testing capabilities for the project. --- microsphere-spring-cloud-openfeign/pom.xml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/pom.xml b/microsphere-spring-cloud-openfeign/pom.xml index cf5f9dd1..556c28bf 100644 --- a/microsphere-spring-cloud-openfeign/pom.xml +++ b/microsphere-spring-cloud-openfeign/pom.xml @@ -50,12 +50,20 @@ true + org.springframework.boot spring-boot-starter-test test - + + + + io.github.microsphere-projects + microsphere-spring-test + test + + \ No newline at end of file From 23273f18ea9ef7f071b1cb517421e49d5f19bcb1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 13:45:08 +0800 Subject: [PATCH 154/310] Refactor to use static import for instantiate method Replaces usage of DecoratedFeignComponent.instantiate with a static import of the instantiate method in AutoRefreshCapability. Cleans up imports and improves code readability. --- .../autorefresh/AutoRefreshCapability.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index dbd23182..95571edc 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -12,7 +12,6 @@ import io.microsphere.spring.cloud.openfeign.components.DecoratedDecoder; import io.microsphere.spring.cloud.openfeign.components.DecoratedEncoder; import io.microsphere.spring.cloud.openfeign.components.DecoratedErrorDecoder; -import io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent; import io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder; import io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer; import org.springframework.beans.BeansException; @@ -22,6 +21,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; + /** * @author 韩超 * @since 0.0.1 @@ -53,7 +54,7 @@ public Retryer enrich(Retryer retryer) { if (retryer == null) return null; - DecoratedRetryer decoratedRetryer = DecoratedFeignComponent.instantiate(DecoratedRetryer.class, Retryer.class, + DecoratedRetryer decoratedRetryer = instantiate(DecoratedRetryer.class, Retryer.class, contextId, contextFactory, clientProperties, retryer); this.componentRegistry.register(contextId, decoratedRetryer); @@ -65,7 +66,7 @@ public Contract enrich(Contract contract) { if (contract == null) return null; - DecoratedContract decoratedContract = DecoratedFeignComponent.instantiate(DecoratedContract.class, Contract.class, + DecoratedContract decoratedContract = instantiate(DecoratedContract.class, Contract.class, contextId, contextFactory, clientProperties, contract); this.componentRegistry.register(contextId, decoratedContract); return decoratedContract; @@ -76,7 +77,7 @@ public Decoder enrich(Decoder decoder) { if (decoder == null) return null; - DecoratedDecoder decoratedDecoder = DecoratedFeignComponent.instantiate(DecoratedDecoder.class, Decoder.class, + DecoratedDecoder decoratedDecoder = instantiate(DecoratedDecoder.class, Decoder.class, contextId, contextFactory, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedDecoder); return decoratedDecoder; @@ -87,7 +88,7 @@ public Encoder enrich(Encoder encoder) { if (encoder == null) return null; - DecoratedEncoder decoratedEncoder = DecoratedFeignComponent.instantiate(DecoratedEncoder.class, Encoder.class, + DecoratedEncoder decoratedEncoder = instantiate(DecoratedEncoder.class, Encoder.class, contextId, contextFactory, clientProperties, encoder); this.componentRegistry.register(contextId, decoratedEncoder); return decoratedEncoder; @@ -97,7 +98,7 @@ public ErrorDecoder enrich(ErrorDecoder decoder) { if (decoder == null) return null; - DecoratedErrorDecoder decoratedErrorDecoder = DecoratedFeignComponent.instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, + DecoratedErrorDecoder decoratedErrorDecoder = instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, contextId, contextFactory, clientProperties, decoder); this.componentRegistry.register(contextId, decoratedErrorDecoder); return decoratedErrorDecoder; @@ -113,11 +114,10 @@ public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { if (queryMapEncoder == null) return null; - DecoratedQueryMapEncoder decoratedQueryMapEncoder = DecoratedFeignComponent.instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, + DecoratedQueryMapEncoder decoratedQueryMapEncoder = instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, contextId, contextFactory, clientProperties, queryMapEncoder); this.componentRegistry.register(contextId, decoratedQueryMapEncoder); return decoratedQueryMapEncoder; } - } From 9c79e48abdf9c0e6f7732eb2cf053496b6c9b34c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 13:45:15 +0800 Subject: [PATCH 155/310] Add abstract test for DecoratedFeignComponent Introduces DecoratedFeignComponentTest as an abstract base class for testing DecoratedFeignComponent. Sets up common test fields and initialization for Feign client context and properties. --- .../DecoratedFeignComponentTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java new file mode 100644 index 00000000..d320424d --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.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 io.microsphere.spring.cloud.openfeign.components; + +import org.junit.jupiter.api.BeforeEach; +import org.springframework.cloud.context.named.NamedContextFactory; +import org.springframework.cloud.openfeign.FeignClientFactory; +import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientSpecification; + +/** + * Abstract {@link DecoratedFeignComponent} Test + * + * @author Mercy + * @see DecoratedFeignComponent + * @since 1.0.0 + */ +abstract class DecoratedFeignComponentTest { + + protected String contextId; + + protected NamedContextFactory contextFactory; + + protected FeignClientProperties clientProperties; + + @BeforeEach + void setUp() { + this.contextId = "default"; + this.contextFactory = new FeignClientFactory(); + this.clientProperties = new FeignClientProperties(); + } +} From b4c963f7a633a882d52729a98a33a683f5a5eb6a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 13:45:17 +0800 Subject: [PATCH 156/310] Create DecoratedContractTest.java --- .../components/DecoratedContractTest.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java new file mode 100644 index 00000000..30064592 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.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 io.microsphere.spring.cloud.openfeign.components; + + +import feign.Contract; +import feign.MethodMetadata; +import io.microsphere.spring.cloud.openfeign.BaseClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.openfeign.support.SpringMvcContract; + +import java.util.List; + +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link DecoratedContract} Test + * + * @author Mercy + * @see DecoratedContract + * @since 1.0.0 + */ +class DecoratedContractTest extends DecoratedFeignComponentTest { + + private Contract delegate; + + private DecoratedContract decoratedContract; + + @BeforeEach + void setUp() { + super.setUp(); + this.delegate = new SpringMvcContract(); + this.decoratedContract = instantiate(DecoratedContract.class, Contract.class, this.contextId, + this.contextFactory, this.clientProperties, this.delegate); + } + + @Test + void testComponentType() { + assertEquals(Contract.class, decoratedContract.componentType()); + } + + @Test + void testParseAndValidateMetadata() { + assertMethodMetadataList(delegate.parseAndValidateMetadata(BaseClient.class), + decoratedContract.parseAndValidateMetadata(BaseClient.class)); + } + + void assertMethodMetadataList(List one, List another) { + assertEquals(one.size(), another.size()); + for (int i = 0; i < one.size(); i++) { + MethodMetadata oneMethodMetadata = one.get(i); + MethodMetadata anotherMethodMetadata = another.get(i); + assertMethodMetadata(oneMethodMetadata, anotherMethodMetadata); + } + } + + void assertMethodMetadata(MethodMetadata one, MethodMetadata another) { + assertEquals(one.method(), another.method()); + } +} \ No newline at end of file From 0a4947dbbe8b0b4e6edbd4d6723e994afed477fc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 13:46:16 +0800 Subject: [PATCH 157/310] Format AutoRefreshCapability constructor parameters Improved readability by splitting the constructor parameters of AutoRefreshCapability onto separate lines. --- .../cloud/openfeign/autorefresh/AutoRefreshCapability.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 95571edc..78235ec1 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -37,7 +37,9 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAwar private String contextId; - public AutoRefreshCapability(FeignClientProperties clientProperties, NamedContextFactory contextFactory, FeignComponentRegistry componentRegistry) { + public AutoRefreshCapability(FeignClientProperties clientProperties, + NamedContextFactory contextFactory, + FeignComponentRegistry componentRegistry) { this.clientProperties = clientProperties; this.contextFactory = contextFactory; this.componentRegistry = componentRegistry; From 0a90019a362ea2294eff7829a26207811e0ffb39 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 14:09:24 +0800 Subject: [PATCH 158/310] Refactor logging to use microsphere logging API Replaced SLF4J Logger and LoggerFactory with io.microsphere.logging.Logger and LoggerFactory. Updated logger field and related method calls to use the new logging API for consistency across the project. --- .../components/DecoratedFeignComponent.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 4959abbe..0a36fa70 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -1,7 +1,6 @@ package io.microsphere.spring.cloud.openfeign.components; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; import org.springframework.cloud.context.named.NamedContextFactory; @@ -12,29 +11,34 @@ import java.lang.reflect.Constructor; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.function.Function; +import static io.microsphere.logging.LoggerFactory.getLogger; + /** * @author 韩超 * @since 0.0.1 */ public abstract class DecoratedFeignComponent implements Refreshable { - private final Logger log = LoggerFactory.getLogger(getClass()); + protected final Logger logger = getLogger(getClass()); //private final FeignClientFactory feignClientFactory; private final NamedContextFactory contextFactory; + private final String contextId; private final FeignClientProperties clientProperties; - protected volatile T delegate; private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); - private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + private final ReadLock readLock = lock.readLock(); + + private final WriteLock writeLock = lock.writeLock(); public DecoratedFeignComponent(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { this.contextId = contextId; @@ -46,7 +50,7 @@ public DecoratedFeignComponent(String contextId, NamedContextFactory Date: Fri, 24 Oct 2025 14:09:28 +0800 Subject: [PATCH 159/310] Add configuration setup methods to test class Introduces methods to initialize default and current Feign client configurations in DecoratedFeignComponentTest. Also updates contextId initialization for improved test context management. --- .../DecoratedFeignComponentTest.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index d320424d..1335a613 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -21,8 +21,11 @@ import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; +import java.util.Map; + /** * Abstract {@link DecoratedFeignComponent} Test * @@ -40,8 +43,22 @@ abstract class DecoratedFeignComponentTest { @BeforeEach void setUp() { - this.contextId = "default"; + this.contextId = "test-context"; this.contextFactory = new FeignClientFactory(); this.clientProperties = new FeignClientProperties(); } -} + + void initDefaultConfiguration() { + String defaultConfig = this.clientProperties.getDefaultConfig(); + setConfiguration(defaultConfig, new FeignClientConfiguration()); + } + + void initCurrentConfiguration() { + setConfiguration(this.contextId, new FeignClientConfiguration()); + } + + void setConfiguration(String id, FeignClientConfiguration configuration) { + Map config = this.clientProperties.getConfig(); + config.put(id, configuration); + } +} \ No newline at end of file From 312450a37c34ac2163a0040a438d94d0706bb503 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 16:40:20 +0800 Subject: [PATCH 160/310] Add unit tests for DecoratedDecoder component Introduces DecoratedDecoderTest to verify the behavior of the DecoratedDecoder class, including component type resolution and decode functionality using SpringDecoder as the delegate. --- .../components/DecoratedDecoderTest.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java new file mode 100644 index 00000000..48eaa9f8 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.openfeign.components; + + +import feign.Request; +import feign.Response; +import feign.codec.Decoder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.support.SpringDecoder; + +import java.io.IOException; + +import static feign.Request.create; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link DecoratedDecoder} Test + * + * @author Mercy + * @see DecoratedDecoder + * @since 1.0.0 + */ +class DecoratedDecoderTest extends DecoratedFeignComponentTest { + + private Decoder delegate; + + private DecoratedDecoder decoratedDecoder; + + @BeforeEach + void setUp() { + super.setUp(); + HttpMessageConverters httpMessageConverters = new HttpMessageConverters(); + ObjectFactory messageConverters = () -> httpMessageConverters; + this.delegate = new SpringDecoder(messageConverters); + this.decoratedDecoder = instantiate(DecoratedDecoder.class, Decoder.class, contextId, contextFactory, + clientProperties, delegate); + } + + @Test + void testComponentTypeFromDefaultConfiguration() { + initDefaultConfiguration(); + this.decoratedDecoder.getDefaultConfiguration().setDecoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedDecoder.componentType()); + } + + @Test + void testComponentTypeFromCurrentConfiguration() { + initCurrentConfiguration(); + this.decoratedDecoder.getCurrentConfiguration().setDecoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedDecoder.componentType()); + } + + @Test + void testComponentType() { + assertSame(Decoder.class, this.decoratedDecoder.componentType()); + } + + @Test + void testDecode() throws IOException { + Request request = create(Request.HttpMethod.GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, + null, null); + Response response = Response.builder() + .status(200) + .reason("OK") + .request(request) + .build(); + + assertEquals(this.decoratedDecoder.decode(response, String.class), this.delegate.decode(response, String.class)); + } +} \ No newline at end of file From 01adce0990cb5eba270ffd16c009d35a971d1040 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 16:49:42 +0800 Subject: [PATCH 161/310] Add unit tests for DecoratedEncoder component Introduces DecoratedEncoderTest to verify the behavior of the DecoratedEncoder class, including component type resolution and encoding functionality. --- .../components/DecoratedEncoderTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.java new file mode 100644 index 00000000..86bd5fda --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.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 io.microsphere.spring.cloud.openfeign.components; + + +import feign.RequestTemplate; +import feign.codec.Encoder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.support.SpringEncoder; + +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link DecoratedEncoder} Test + * + * @author Mercy + * @see DecoratedEncoder + * @since 1.0.0 + */ +class DecoratedEncoderTest extends DecoratedFeignComponentTest { + + private Encoder delegate; + + private DecoratedEncoder decoratedEncoder; + + @BeforeEach + void setUp() { + super.setUp(); + HttpMessageConverters httpMessageConverters = new HttpMessageConverters(); + ObjectFactory messageConverters = () -> httpMessageConverters; + this.delegate = new SpringEncoder(messageConverters); + this.decoratedEncoder = instantiate(DecoratedEncoder.class, Encoder.class, contextId, contextFactory, + clientProperties, delegate); + } + + @Test + void testComponentTypeFromDefaultConfiguration() { + initDefaultConfiguration(); + this.decoratedEncoder.getDefaultConfiguration().setEncoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedEncoder.componentType()); + } + + @Test + void testComponentTypeFromCurrentConfiguration() { + initCurrentConfiguration(); + this.decoratedEncoder.getCurrentConfiguration().setEncoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedEncoder.componentType()); + } + + @Test + void testComponentType() { + assertSame(Encoder.class, this.decoratedEncoder.componentType()); + } + + @Test + void testEncode() { + RequestTemplate template = new RequestTemplate(); + String value = "Test"; + this.decoratedEncoder.encode(value, String.class, template); + byte[] body = template.body(); + assertArrayEquals(value.getBytes(template.requestCharset()), body); + } +} \ No newline at end of file From b2abbc8ad73383545a44f7d02c894869fca27f12 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 17:00:08 +0800 Subject: [PATCH 162/310] Refactor DecoratedDecoderTest to use Response.builder Replaced direct usage of Response.builder() with static import and added a body to the response in the testDecode method for improved clarity and consistency. --- .../cloud/openfeign/components/DecoratedDecoderTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java index 48eaa9f8..f6b457ff 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java @@ -30,6 +30,7 @@ import java.io.IOException; import static feign.Request.create; +import static feign.Response.builder; import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; import static java.util.Collections.emptyMap; @@ -82,10 +83,11 @@ void testComponentType() { void testDecode() throws IOException { Request request = create(Request.HttpMethod.GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, null, null); - Response response = Response.builder() + + Response response = builder() .status(200) - .reason("OK") .request(request) + .body(new byte[1024]) .build(); assertEquals(this.decoratedDecoder.decode(response, String.class), this.delegate.decode(response, String.class)); From 2ad8c8dcdbf3167e8b4d7852cf2a6c73ecb20552 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 17:10:05 +0800 Subject: [PATCH 163/310] Add unit tests for DecoratedErrorDecoder Introduces DecoratedErrorDecoderTest to verify component type resolution and decode behavior for DecoratedErrorDecoder. Ensures correct integration with configuration and delegate ErrorDecoder. --- .../components/DecoratedErrorDecoderTest.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java new file mode 100644 index 00000000..ecd6fbcf --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.openfeign.components; + + +import feign.Request; +import feign.Response; +import feign.codec.ErrorDecoder; +import feign.codec.ErrorDecoder.Default; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static feign.Request.create; +import static feign.Response.builder; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link DecoratedErrorDecoder} Test + * + * @author Mercy + * @see DecoratedErrorDecoder + * @since 1.0.0 + */ +class DecoratedErrorDecoderTest extends DecoratedFeignComponentTest { + + private ErrorDecoder delegate; + + private DecoratedErrorDecoder decoratedErrorDecoder; + + @BeforeEach + void setUp() { + super.setUp(); + this.delegate = new Default(); + this.decoratedErrorDecoder = instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, contextId, contextFactory, + clientProperties, delegate); + } + + @Test + void testComponentTypeFromDefaultConfiguration() { + initDefaultConfiguration(); + this.decoratedErrorDecoder.getDefaultConfiguration().setErrorDecoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedErrorDecoder.componentType()); + } + + @Test + void testComponentTypeFromCurrentConfiguration() { + initCurrentConfiguration(); + this.decoratedErrorDecoder.getCurrentConfiguration().setErrorDecoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedErrorDecoder.componentType()); + } + + @Test + void testComponentType() { + assertSame(ErrorDecoder.class, this.decoratedErrorDecoder.componentType()); + } + + @Test + void testDecode() { + Request request = create(Request.HttpMethod.GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, + null, null); + + Response response = builder() + .status(200) + .request(request) + .body(new byte[1024]) + .build(); + + assertTrue(this.decoratedErrorDecoder.decode("echo", response) instanceof Exception); + } +} \ No newline at end of file From 00f1510edc7c10c71b40443816b1fe5599ae1b4d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 17:20:18 +0800 Subject: [PATCH 164/310] Refactor FeignClientConfiguration import usage Simplified references to FeignClientConfiguration by importing it directly, improving code readability and maintainability in CompositedRequestInterceptor. --- .../openfeign/components/CompositedRequestInterceptor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index c4d40656..297acd72 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -5,6 +5,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.util.CollectionUtils; import java.util.Collection; @@ -69,8 +70,8 @@ public void refresh() { Map> headers = new HashMap<>(); Map> params = new HashMap<>(); if (properties != null) { - FeignClientProperties.FeignClientConfiguration defaultConfiguration = properties.getConfig().get(properties.getDefaultConfig()); - FeignClientProperties.FeignClientConfiguration current = properties.getConfig().get(contextId); + FeignClientConfiguration defaultConfiguration = properties.getConfig().get(properties.getDefaultConfig()); + FeignClientConfiguration current = properties.getConfig().get(contextId); if (defaultConfiguration != null && defaultConfiguration.getRequestInterceptors() != null) interceptors.addAll(defaultConfiguration.getRequestInterceptors()); if (current != null && current.getRequestInterceptors() != null) From dabbe62763343d5da2841ce22705c052940c902e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 17:20:23 +0800 Subject: [PATCH 165/310] Refactor usage of FeignClientConfiguration import Replaced references to FeignClientProperties.FeignClientConfiguration with FeignClientConfiguration for clarity and consistency. Updated method signatures and method handle initialization accordingly. --- .../cloud/openfeign/components/DecoratedQueryMapEncoder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index c40c1400..133a5311 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -4,6 +4,7 @@ import io.microsphere.logging.Logger; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; import java.lang.invoke.MethodHandle; @@ -22,7 +23,7 @@ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent contextFactory, FeignClientProperties clientProperties, QueryMapEncoder delegate) { super(contextId, contextFactory, clientProperties, delegate); @@ -37,7 +38,7 @@ protected Class componentType() { return queryMapEncoderClass == null ? QueryMapEncoder.class : queryMapEncoderClass; } - private Class getQueryMapEncoder(FeignClientProperties.FeignClientConfiguration feignClientConfiguration) { + private Class getQueryMapEncoder(FeignClientConfiguration feignClientConfiguration) { if (feignClientConfiguration == null || getQueryMapEncoderMethodHandle == null) { return null; } From adea77cab47c9b51f66c5d2eba2fab70ea06d51a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 17:27:24 +0800 Subject: [PATCH 166/310] Refactor getQueryMapEncoder method for clarity Extracted getQueryMapEncoder logic into a static method and improved error handling using handleInvokeExactFailure. Also replaced null check with NOT_FOUND_METHOD_HANDLE for better robustness. --- .../components/DecoratedQueryMapEncoder.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index 133a5311..a2cb27d3 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -11,6 +11,8 @@ import java.util.Map; import static io.microsphere.invoke.MethodHandleUtils.findVirtual; +import static io.microsphere.invoke.MethodHandleUtils.handleInvokeExactFailure; +import static io.microsphere.invoke.MethodHandlesLookupUtils.NOT_FOUND_METHOD_HANDLE; import static io.microsphere.logging.LoggerFactory.getLogger; /** @@ -23,7 +25,7 @@ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent contextFactory, FeignClientProperties clientProperties, QueryMapEncoder delegate) { super(contextId, contextFactory, clientProperties, delegate); @@ -39,20 +41,23 @@ protected Class componentType() { } private Class getQueryMapEncoder(FeignClientConfiguration feignClientConfiguration) { - if (feignClientConfiguration == null || getQueryMapEncoderMethodHandle == null) { + return getQueryMapEncoder(getQueryMapEncoderMethodHandle, feignClientConfiguration); + } + + static Class getQueryMapEncoder(MethodHandle methodHandle, FeignClientConfiguration feignClientConfiguration) { + if (methodHandle == NOT_FOUND_METHOD_HANDLE) { return null; } Class queryMapEncoderClass = null; try { queryMapEncoderClass = (Class) getQueryMapEncoderMethodHandle.invokeExact(feignClientConfiguration); } catch (Throwable e) { - if (logger.isWarnEnabled()) { - logger.warn("FeignClientProperties.FeignClientConfiguration#getQueryMapEncoder() method can't be invoked , instance : {}", feignClientConfiguration); - } + handleInvokeExactFailure(e, getQueryMapEncoderMethodHandle, feignClientConfiguration); } return queryMapEncoderClass; } + @Override public Map encode(Object object) { return delegate().encode(object); From 231ed970598a44f8bbcb61e74cd442e31b62780d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 17:27:28 +0800 Subject: [PATCH 167/310] Add unit tests for DecoratedQueryMapEncoder Introduces DecoratedQueryMapEncoderTest to verify component type resolution and encoding behavior. Ensures correct handling of default and current configurations, and validates utility methods. --- .../DecoratedQueryMapEncoderTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.java new file mode 100644 index 00000000..73cab6dc --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.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 io.microsphere.spring.cloud.openfeign.components; + + +import feign.QueryMapEncoder; +import feign.querymap.BeanQueryMapEncoder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder.getQueryMapEncoder; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder.getQueryMapEncoderMethodHandle; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link DecoratedQueryMapEncoder} Test + * + * @author Mercy + * @see DecoratedQueryMapEncoder + * @since 1.0.0 + */ +class DecoratedQueryMapEncoderTest extends DecoratedFeignComponentTest { + + private QueryMapEncoder delegate; + + private DecoratedQueryMapEncoder decoratedQueryMapEncoder; + + @BeforeEach + void setUp() { + super.setUp(); + this.delegate = new BeanQueryMapEncoder(); + this.decoratedQueryMapEncoder = instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, this.contextId, + this.contextFactory, this.clientProperties, this.delegate); + } + + @Test + void testComponentTypeFromDefaultConfiguration() { + initDefaultConfiguration(); + this.decoratedQueryMapEncoder.getDefaultConfiguration().setQueryMapEncoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedQueryMapEncoder.componentType()); + } + + @Test + void testComponentTypeFromCurrentConfiguration() { + initCurrentConfiguration(); + this.decoratedQueryMapEncoder.getCurrentConfiguration().setQueryMapEncoder((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedQueryMapEncoder.componentType()); + } + + @Test + void testComponentType() { + assertSame(QueryMapEncoder.class, this.decoratedQueryMapEncoder.componentType()); + } + + @Test + void testEncode() { + assertSame(emptyMap(), this.decoratedQueryMapEncoder.encode(null)); + } + + @Test + void testGetQueryMapEncoder() { + assertNull(getQueryMapEncoder(null, null)); + assertNull(getQueryMapEncoder(getQueryMapEncoderMethodHandle, null)); + } +} \ No newline at end of file From d6574756885a73b385ee751fd014b302ea754766 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 19:22:42 +0800 Subject: [PATCH 168/310] Add tests for DecoratedRetryer and improve component instantiation Added DecoratedRetryerTest to cover component type and continueOrPropagate logic. Updated DecoratedFeignComponent.instantiate to support Class for componentClass. Refactored DecoratedRetryer to extract continueOrPropagate logic. Improved test setup in DecoratedFeignComponentTest and DecoratedContractTest for better coverage and initialization. --- .../components/DecoratedFeignComponent.java | 2 +- .../components/DecoratedRetryer.java | 8 +- .../components/DecoratedContractTest.java | 17 ++- .../DecoratedFeignComponentTest.java | 10 +- .../components/DecoratedRetryerTest.java | 103 ++++++++++++++++++ 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 0a36fa70..a14dd5ce 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -135,7 +135,7 @@ public String toString() { return delegate().toString(); } - public static , T> W instantiate(Class decoratedClass, Class componentClass, + public static , T> W instantiate(Class decoratedClass, Class componentClass, String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { try { Constructor constructor = decoratedClass.getConstructor(String.class, NamedContextFactory.class, FeignClientProperties.class, componentClass); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java index 32441dea..79989b41 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java @@ -25,9 +25,13 @@ protected Class componentType() { @Override public void continueOrPropagate(RetryableException e) { - Retryer retryer = delegate(); - if (retryer != null) + continueOrPropagate(delegate(), e); + } + + static void continueOrPropagate(Retryer retryer, RetryableException e) { + if (retryer != null) { retryer.continueOrPropagate(e); + } } @Override diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java index 30064592..acef096f 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java @@ -29,6 +29,7 @@ import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link DecoratedContract} Test @@ -51,9 +52,23 @@ void setUp() { this.contextFactory, this.clientProperties, this.delegate); } + @Test + void testComponentTypeFromDefaultConfiguration() { + initDefaultConfiguration(); + this.decoratedContract.getDefaultConfiguration().setContract((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedContract.componentType()); + } + + @Test + void testComponentTypeFromCurrentConfiguration() { + initCurrentConfiguration(); + this.decoratedContract.getCurrentConfiguration().setContract((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedContract.componentType()); + } + @Test void testComponentType() { - assertEquals(Contract.class, decoratedContract.componentType()); + assertSame(Contract.class, this.decoratedContract.componentType()); } @Test diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index 1335a613..823f2f35 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -23,9 +23,13 @@ import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.support.GenericApplicationContext; import java.util.Map; +import static io.microsphere.collection.MapUtils.newHashMap; + /** * Abstract {@link DecoratedFeignComponent} Test * @@ -44,10 +48,14 @@ abstract class DecoratedFeignComponentTest { @BeforeEach void setUp() { this.contextId = "test-context"; - this.contextFactory = new FeignClientFactory(); + this.contextFactory = new FeignClientFactory(applicationContextInitializers()); this.clientProperties = new FeignClientProperties(); } + protected Map> applicationContextInitializers() { + return newHashMap(); + } + void initDefaultConfiguration() { String defaultConfig = this.clientProperties.getDefaultConfig(); setConfiguration(defaultConfig, new FeignClientConfiguration()); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java new file mode 100644 index 00000000..2307fce5 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.openfeign.components; + + +import feign.Request; +import feign.RetryableException; +import feign.Retryer; +import feign.Retryer.Default; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.Date; +import java.util.Map; + +import static feign.Request.create; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer.continueOrPropagate; +import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link DecoratedRetryer} Test + * + * @author Mercy + * @see DecoratedRetryer + * @since 1.0.0 + */ +class DecoratedRetryerTest extends DecoratedFeignComponentTest { + + private Retryer delegate; + + private DecoratedRetryer decoratedRetryer; + + @BeforeEach + void setUp() { + super.setUp(); + this.delegate = new Default(); + this.decoratedRetryer = instantiate(DecoratedRetryer.class, Retryer.class, this.contextId, this.contextFactory, + this.clientProperties, null); + } + + @Override + protected Map> applicationContextInitializers() { + Map> initializers = super.applicationContextInitializers(); + initializers.put(this.contextId, context -> { + context.registerBean(Retryer.class, () -> delegate); + }); + return initializers; + } + + @Test + void testComponentTypeFromDefaultConfiguration() { + initDefaultConfiguration(); + this.decoratedRetryer.getDefaultConfiguration().setRetryer((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedRetryer.componentType()); + } + + @Test + void testComponentTypeFromCurrentConfiguration() { + initCurrentConfiguration(); + this.decoratedRetryer.getCurrentConfiguration().setRetryer((Class) this.delegate.getClass()); + assertSame(this.delegate.getClass(), this.decoratedRetryer.componentType()); + } + + @Test + void testComponentType() { + assertSame(Retryer.class, this.decoratedRetryer.componentType()); + } + + @Test + void testContinueOrPropagate() { + Request request = create(Request.HttpMethod.GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, + null, null); + RetryableException e = new RetryableException(1, "error", Request.HttpMethod.GET, new Date(), request); + this.decoratedRetryer.continueOrPropagate(e); + continueOrPropagate(null, e); + } + + @Test + void testClone() { + assertNotNull(this.decoratedRetryer.clone()); + } +} \ No newline at end of file From cf9a3b6cab497afdec4c9f1ac5a47620c651fee7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 19:57:51 +0800 Subject: [PATCH 169/310] Refactor DecoratedFeignComponentTest for generics reuse Refactored DecoratedFeignComponentTest to use generics, reducing code duplication across component tests. Updated all Decorated* test classes to extend the new generic base, moving common setup and assertions into the base class. This improves maintainability and consistency of the test suite. --- .../components/DecoratedContractTest.java | 44 +++------- .../components/DecoratedDecoderTest.java | 56 +++---------- .../components/DecoratedEncoderTest.java | 41 ++------- .../components/DecoratedErrorDecoderTest.java | 57 +++---------- .../DecoratedFeignComponentTest.java | 83 ++++++++++++++++++- .../DecoratedQueryMapEncoderTest.java | 40 ++------- .../components/DecoratedRetryerTest.java | 64 ++++---------- 7 files changed, 144 insertions(+), 241 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java index acef096f..f5381722 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java @@ -21,15 +21,13 @@ import feign.Contract; import feign.MethodMetadata; import io.microsphere.spring.cloud.openfeign.BaseClient; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.support.SpringMvcContract; import java.util.List; -import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link DecoratedContract} Test @@ -38,43 +36,21 @@ * @see DecoratedContract * @since 1.0.0 */ -class DecoratedContractTest extends DecoratedFeignComponentTest { - - private Contract delegate; - - private DecoratedContract decoratedContract; - - @BeforeEach - void setUp() { - super.setUp(); - this.delegate = new SpringMvcContract(); - this.decoratedContract = instantiate(DecoratedContract.class, Contract.class, this.contextId, - this.contextFactory, this.clientProperties, this.delegate); - } - - @Test - void testComponentTypeFromDefaultConfiguration() { - initDefaultConfiguration(); - this.decoratedContract.getDefaultConfiguration().setContract((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedContract.componentType()); +class DecoratedContractTest extends DecoratedFeignComponentTest { + @Override + protected Contract createDelegate() { + return new SpringMvcContract(); } - @Test - void testComponentTypeFromCurrentConfiguration() { - initCurrentConfiguration(); - this.decoratedContract.getCurrentConfiguration().setContract((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedContract.componentType()); - } - - @Test - void testComponentType() { - assertSame(Contract.class, this.decoratedContract.componentType()); + @Override + protected void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass) { + configuration.setContract(delegateClass); } @Test void testParseAndValidateMetadata() { - assertMethodMetadataList(delegate.parseAndValidateMetadata(BaseClient.class), - decoratedContract.parseAndValidateMetadata(BaseClient.class)); + assertMethodMetadataList(this.delegate.parseAndValidateMetadata(BaseClient.class), + this.decoratedComponent.parseAndValidateMetadata(BaseClient.class)); } void assertMethodMetadataList(List one, List another) { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java index f6b457ff..b6169ccd 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoderTest.java @@ -18,24 +18,17 @@ package io.microsphere.spring.cloud.openfeign.components; -import feign.Request; import feign.Response; import feign.codec.Decoder; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.support.SpringDecoder; import java.io.IOException; -import static feign.Request.create; -import static feign.Response.builder; -import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; -import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; -import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link DecoratedDecoder} Test @@ -44,52 +37,23 @@ * @see DecoratedDecoder * @since 1.0.0 */ -class DecoratedDecoderTest extends DecoratedFeignComponentTest { +class DecoratedDecoderTest extends DecoratedFeignComponentTest { - private Decoder delegate; - - private DecoratedDecoder decoratedDecoder; - - @BeforeEach - void setUp() { - super.setUp(); + @Override + protected Decoder createDelegate() { HttpMessageConverters httpMessageConverters = new HttpMessageConverters(); ObjectFactory messageConverters = () -> httpMessageConverters; - this.delegate = new SpringDecoder(messageConverters); - this.decoratedDecoder = instantiate(DecoratedDecoder.class, Decoder.class, contextId, contextFactory, - clientProperties, delegate); - } - - @Test - void testComponentTypeFromDefaultConfiguration() { - initDefaultConfiguration(); - this.decoratedDecoder.getDefaultConfiguration().setDecoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedDecoder.componentType()); + return new SpringDecoder(messageConverters); } - @Test - void testComponentTypeFromCurrentConfiguration() { - initCurrentConfiguration(); - this.decoratedDecoder.getCurrentConfiguration().setDecoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedDecoder.componentType()); - } - - @Test - void testComponentType() { - assertSame(Decoder.class, this.decoratedDecoder.componentType()); + @Override + protected void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass) { + configuration.setDecoder(delegateClass); } @Test void testDecode() throws IOException { - Request request = create(Request.HttpMethod.GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, - null, null); - - Response response = builder() - .status(200) - .request(request) - .body(new byte[1024]) - .build(); - - assertEquals(this.decoratedDecoder.decode(response, String.class), this.delegate.decode(response, String.class)); + Response response = createTestResponse(); + assertEquals(this.decoratedComponent.decode(response, String.class), this.delegate.decode(response, String.class)); } } \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.java index 86bd5fda..ab90d283 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoderTest.java @@ -20,15 +20,13 @@ import feign.RequestTemplate; import feign.codec.Encoder; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.support.SpringEncoder; -import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link DecoratedEncoder} Test @@ -37,46 +35,25 @@ * @see DecoratedEncoder * @since 1.0.0 */ -class DecoratedEncoderTest extends DecoratedFeignComponentTest { +class DecoratedEncoderTest extends DecoratedFeignComponentTest { - private Encoder delegate; - - private DecoratedEncoder decoratedEncoder; - - @BeforeEach - void setUp() { - super.setUp(); + @Override + protected Encoder createDelegate() { HttpMessageConverters httpMessageConverters = new HttpMessageConverters(); ObjectFactory messageConverters = () -> httpMessageConverters; - this.delegate = new SpringEncoder(messageConverters); - this.decoratedEncoder = instantiate(DecoratedEncoder.class, Encoder.class, contextId, contextFactory, - clientProperties, delegate); - } - - @Test - void testComponentTypeFromDefaultConfiguration() { - initDefaultConfiguration(); - this.decoratedEncoder.getDefaultConfiguration().setEncoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedEncoder.componentType()); + return new SpringEncoder(messageConverters); } - @Test - void testComponentTypeFromCurrentConfiguration() { - initCurrentConfiguration(); - this.decoratedEncoder.getCurrentConfiguration().setEncoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedEncoder.componentType()); - } - - @Test - void testComponentType() { - assertSame(Encoder.class, this.decoratedEncoder.componentType()); + @Override + protected void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass) { + configuration.setEncoder(delegateClass); } @Test void testEncode() { RequestTemplate template = new RequestTemplate(); String value = "Test"; - this.decoratedEncoder.encode(value, String.class, template); + this.decoratedComponent.encode(value, String.class, template); byte[] body = template.body(); assertArrayEquals(value.getBytes(template.requestCharset()), body); } diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java index ecd6fbcf..58fac610 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java @@ -18,19 +18,12 @@ package io.microsphere.spring.cloud.openfeign.components; -import feign.Request; import feign.Response; import feign.codec.ErrorDecoder; import feign.codec.ErrorDecoder.Default; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; -import static feign.Request.create; -import static feign.Response.builder; -import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; -import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; -import static java.util.Collections.emptyMap; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -40,50 +33,20 @@ * @see DecoratedErrorDecoder * @since 1.0.0 */ -class DecoratedErrorDecoderTest extends DecoratedFeignComponentTest { - - private ErrorDecoder delegate; - - private DecoratedErrorDecoder decoratedErrorDecoder; - - @BeforeEach - void setUp() { - super.setUp(); - this.delegate = new Default(); - this.decoratedErrorDecoder = instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, contextId, contextFactory, - clientProperties, delegate); - } - - @Test - void testComponentTypeFromDefaultConfiguration() { - initDefaultConfiguration(); - this.decoratedErrorDecoder.getDefaultConfiguration().setErrorDecoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedErrorDecoder.componentType()); +class DecoratedErrorDecoderTest extends DecoratedFeignComponentTest { + @Override + protected ErrorDecoder createDelegate() { + return new Default(); } - @Test - void testComponentTypeFromCurrentConfiguration() { - initCurrentConfiguration(); - this.decoratedErrorDecoder.getCurrentConfiguration().setErrorDecoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedErrorDecoder.componentType()); - } - - @Test - void testComponentType() { - assertSame(ErrorDecoder.class, this.decoratedErrorDecoder.componentType()); + @Override + protected void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass) { + configuration.setErrorDecoder(delegateClass); } @Test void testDecode() { - Request request = create(Request.HttpMethod.GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, - null, null); - - Response response = builder() - .status(200) - .request(request) - .body(new byte[1024]) - .build(); - - assertTrue(this.decoratedErrorDecoder.decode("echo", response) instanceof Exception); + Response response = createTestResponse(); + assertTrue(this.decoratedComponent.decode("echo", response) instanceof Exception); } } \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index 823f2f35..62b28d20 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -17,7 +17,11 @@ package io.microsphere.spring.cloud.openfeign.components; +import feign.Request; +import feign.RequestTemplate; +import feign.Response; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientFactory; import org.springframework.cloud.openfeign.FeignClientProperties; @@ -25,10 +29,21 @@ import org.springframework.cloud.openfeign.FeignClientSpecification; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.ResolvableType; import java.util.Map; +import static feign.Request.HttpMethod.GET; +import static feign.Request.create; +import static feign.Response.builder; import static io.microsphere.collection.MapUtils.newHashMap; +import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.springframework.core.ResolvableType.forClass; /** * Abstract {@link DecoratedFeignComponent} Test @@ -37,7 +52,7 @@ * @see DecoratedFeignComponent * @since 1.0.0 */ -abstract class DecoratedFeignComponentTest { +abstract class DecoratedFeignComponentTest> { protected String contextId; @@ -45,17 +60,83 @@ abstract class DecoratedFeignComponentTest { protected FeignClientProperties clientProperties; + protected Class componentClass; + + protected Class decoratedComponentClass; + + protected T delegate; + + protected D decoratedComponent; + @BeforeEach void setUp() { this.contextId = "test-context"; this.contextFactory = new FeignClientFactory(applicationContextInitializers()); this.clientProperties = new FeignClientProperties(); + + ResolvableType resolvableType = forClass(this.getClass()).as(DecoratedFeignComponentTest.class); + this.componentClass = (Class) resolvableType.getGeneric(0).resolve(); + this.decoratedComponentClass = (Class) resolvableType.getGeneric(1).resolve(); + + this.delegate = createDelegate(); + this.decoratedComponent = instantiate(decoratedComponentClass, componentClass, contextId, contextFactory, clientProperties, delegate); + } + + @Test + void testComponentTypeFromDefaultConfiguration() { + initDefaultConfiguration(); + Class delegateClass = getDelegateClass(); + configureDelegateClass(this.decoratedComponent.getDefaultConfiguration(), delegateClass); + assertSame(delegateClass, this.decoratedComponent.componentType()); + } + + @Test + void testComponentTypeFromCurrentConfiguration() { + initCurrentConfiguration(); + Class delegateClass = getDelegateClass(); + configureDelegateClass(this.decoratedComponent.getCurrentConfiguration(), delegateClass); + assertSame(delegateClass, this.decoratedComponent.componentType()); + } + + @Test + void testComponentType() { + assertSame(componentClass, this.decoratedComponent.componentType()); + } + + @Test + void testEquals() { + assertEquals(this.decoratedComponent, this.delegate); + } + + @Test + void testHashCode() { + assertEquals(this.decoratedComponent.hashCode(), this.delegate.hashCode()); + } + + protected abstract T createDelegate(); + + protected abstract void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass); + + protected Class getDelegateClass() { + return (Class) this.delegate.getClass(); } protected Map> applicationContextInitializers() { return newHashMap(); } + protected Request createTestRequest() { + return create(GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, UTF_8, new RequestTemplate()); + } + + protected Response createTestResponse() { + return builder() + .status(200) + .request(createTestRequest()) + .body(new byte[1024]) + .build(); + } + void initDefaultConfiguration() { String defaultConfig = this.clientProperties.getDefaultConfig(); setConfiguration(defaultConfig, new FeignClientConfiguration()); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.java index 73cab6dc..4ff596c2 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoderTest.java @@ -20,10 +20,9 @@ import feign.QueryMapEncoder; import feign.querymap.BeanQueryMapEncoder; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; -import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; import static io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder.getQueryMapEncoder; import static io.microsphere.spring.cloud.openfeign.components.DecoratedQueryMapEncoder.getQueryMapEncoderMethodHandle; import static java.util.Collections.emptyMap; @@ -37,42 +36,21 @@ * @see DecoratedQueryMapEncoder * @since 1.0.0 */ -class DecoratedQueryMapEncoderTest extends DecoratedFeignComponentTest { +class DecoratedQueryMapEncoderTest extends DecoratedFeignComponentTest { - private QueryMapEncoder delegate; - - private DecoratedQueryMapEncoder decoratedQueryMapEncoder; - - @BeforeEach - void setUp() { - super.setUp(); - this.delegate = new BeanQueryMapEncoder(); - this.decoratedQueryMapEncoder = instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, this.contextId, - this.contextFactory, this.clientProperties, this.delegate); - } - - @Test - void testComponentTypeFromDefaultConfiguration() { - initDefaultConfiguration(); - this.decoratedQueryMapEncoder.getDefaultConfiguration().setQueryMapEncoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedQueryMapEncoder.componentType()); + @Override + protected QueryMapEncoder createDelegate() { + return new BeanQueryMapEncoder(); } - @Test - void testComponentTypeFromCurrentConfiguration() { - initCurrentConfiguration(); - this.decoratedQueryMapEncoder.getCurrentConfiguration().setQueryMapEncoder((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedQueryMapEncoder.componentType()); - } - - @Test - void testComponentType() { - assertSame(QueryMapEncoder.class, this.decoratedQueryMapEncoder.componentType()); + @Override + protected void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass) { + configuration.setQueryMapEncoder(delegateClass); } @Test void testEncode() { - assertSame(emptyMap(), this.decoratedQueryMapEncoder.encode(null)); + assertSame(emptyMap(), this.decoratedComponent.encode(null)); } @Test diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java index 2307fce5..b0725ae6 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryerTest.java @@ -22,21 +22,15 @@ import feign.RetryableException; import feign.Retryer; import feign.Retryer.Default; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import java.util.Date; -import java.util.Map; -import static feign.Request.create; -import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; +import static feign.Request.HttpMethod.GET; import static io.microsphere.spring.cloud.openfeign.components.DecoratedRetryer.continueOrPropagate; -import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; -import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * {@link DecoratedRetryer} Test @@ -45,59 +39,29 @@ * @see DecoratedRetryer * @since 1.0.0 */ -class DecoratedRetryerTest extends DecoratedFeignComponentTest { - - private Retryer delegate; - - private DecoratedRetryer decoratedRetryer; - - @BeforeEach - void setUp() { - super.setUp(); - this.delegate = new Default(); - this.decoratedRetryer = instantiate(DecoratedRetryer.class, Retryer.class, this.contextId, this.contextFactory, - this.clientProperties, null); - } +class DecoratedRetryerTest extends DecoratedFeignComponentTest { @Override - protected Map> applicationContextInitializers() { - Map> initializers = super.applicationContextInitializers(); - initializers.put(this.contextId, context -> { - context.registerBean(Retryer.class, () -> delegate); - }); - return initializers; - } - - @Test - void testComponentTypeFromDefaultConfiguration() { - initDefaultConfiguration(); - this.decoratedRetryer.getDefaultConfiguration().setRetryer((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedRetryer.componentType()); - } - - @Test - void testComponentTypeFromCurrentConfiguration() { - initCurrentConfiguration(); - this.decoratedRetryer.getCurrentConfiguration().setRetryer((Class) this.delegate.getClass()); - assertSame(this.delegate.getClass(), this.decoratedRetryer.componentType()); + protected Retryer createDelegate() { + return new Default(); } - @Test - void testComponentType() { - assertSame(Retryer.class, this.decoratedRetryer.componentType()); + @Override + protected void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass) { + configuration.setRetryer(delegateClass); } @Test void testContinueOrPropagate() { - Request request = create(Request.HttpMethod.GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, - null, null); - RetryableException e = new RetryableException(1, "error", Request.HttpMethod.GET, new Date(), request); - this.decoratedRetryer.continueOrPropagate(e); + this.decoratedComponent.refresh(); + Request request = createTestRequest(); + RetryableException e = new RetryableException(1, "error", GET, new Date(), request); + assertThrows(RetryableException.class, () -> this.decoratedComponent.continueOrPropagate(e)); continueOrPropagate(null, e); } @Test void testClone() { - assertNotNull(this.decoratedRetryer.clone()); + assertNotNull(this.decoratedComponent.clone()); } } \ No newline at end of file From 98aa8e509b907367f51a9347cdc363fe4f4d593d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 20:38:41 +0800 Subject: [PATCH 170/310] Remove extra newline in DecoratedContract.java Cleaned up formatting by removing an unnecessary blank line and ensuring no trailing newline at the end of the file. --- .../spring/cloud/openfeign/components/DecoratedContract.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index 6917348c..bbfa7acc 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -25,9 +25,8 @@ protected Class componentType() { return contractClass == null ? Contract.class : contractClass; } - @Override public List parseAndValidateMetadata(Class targetType) { return delegate().parseAndValidateMetadata(targetType); } -} +} \ No newline at end of file From 9592277cd9d6d23d916c69780b2624bd8864eefa Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 20:44:19 +0800 Subject: [PATCH 171/310] Add co-author Mercy to OpenFeign components Added Mercy (Mercy) as a co-author to all relevant component classes in the microsphere-spring-cloud-openfeign module. Minor formatting improvements and JavaDoc updates were also made for consistency. --- .../components/CompositedRequestInterceptor.java | 8 +++----- .../cloud/openfeign/components/DecoratedContract.java | 1 + .../cloud/openfeign/components/DecoratedDecoder.java | 1 + .../cloud/openfeign/components/DecoratedEncoder.java | 6 ++++-- .../cloud/openfeign/components/DecoratedErrorDecoder.java | 3 ++- .../openfeign/components/DecoratedFeignComponent.java | 3 ++- .../openfeign/components/DecoratedQueryMapEncoder.java | 4 ++-- .../cloud/openfeign/components/DecoratedRetryer.java | 3 ++- .../openfeign/components/NoOpRequestInterceptor.java | 6 ++++-- .../spring/cloud/openfeign/components/Refreshable.java | 8 ++++++-- 10 files changed, 27 insertions(+), 16 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index 297acd72..b768a70e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -17,11 +17,13 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class CompositedRequestInterceptor implements RequestInterceptor, Refreshable { private final BeanFactory beanFactory; + private final String contextId; private final Set set = new HashSet<>(); @@ -35,7 +37,6 @@ public Set getRequestInterceptors() { return Collections.unmodifiableSet(set); } - @Override public void apply(RequestTemplate template) { synchronized (this.set) { @@ -118,8 +119,5 @@ public void refresh() { }); }); } - - - } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index bbfa7acc..cedef798 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -11,6 +11,7 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class DecoratedContract extends DecoratedFeignComponent implements Contract { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index 6d230df0..e5e495fd 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -14,6 +14,7 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class DecoratedDecoder extends DecoratedFeignComponent implements Decoder { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index e712bfeb..69d62d76 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -12,11 +12,13 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class DecoratedEncoder extends DecoratedFeignComponent implements Encoder { - public DecoratedEncoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Encoder delegate) { + public DecoratedEncoder(String contextId, NamedContextFactory contextFactory, + FeignClientProperties clientProperties, Encoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } @@ -30,4 +32,4 @@ protected Class componentType() { public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { delegate().encode(object, bodyType, template); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 530bf81d..1f866aae 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -9,6 +9,7 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class DecoratedErrorDecoder extends DecoratedFeignComponent implements ErrorDecoder { @@ -27,4 +28,4 @@ protected Class componentType() { public Exception decode(String methodKey, Response response) { return delegate().decode(methodKey, response); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index a14dd5ce..e64541b0 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -19,6 +19,7 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public abstract class DecoratedFeignComponent implements Refreshable { @@ -145,4 +146,4 @@ public static , T> W instantiate(Class d } } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index a2cb27d3..51902323 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -17,6 +17,7 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent implements QueryMapEncoder { @@ -57,9 +58,8 @@ static Class getQueryMapEncoder(MethodHandle methodHandle, Feig return queryMapEncoderClass; } - @Override public Map encode(Object object) { return delegate().encode(object); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java index 79989b41..0a668331 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java @@ -9,6 +9,7 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class DecoratedRetryer extends DecoratedFeignComponent implements Retryer { @@ -38,4 +39,4 @@ static void continueOrPropagate(Retryer retryer, RetryableException e) { public Retryer clone() { return delegate().clone(); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java index c6e19987..1bc004c7 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java @@ -5,16 +5,18 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class NoOpRequestInterceptor implements RequestInterceptor { public static final NoOpRequestInterceptor INSTANCE = new NoOpRequestInterceptor(); - private NoOpRequestInterceptor() {}; + private NoOpRequestInterceptor() { + } @Override public void apply(RequestTemplate template) { //no op } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/Refreshable.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/Refreshable.java index b4653fe5..482f5353 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/Refreshable.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/Refreshable.java @@ -2,10 +2,14 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ @FunctionalInterface public interface Refreshable { - void refresh(); -} + /** + * Refresh + */ + void refresh(); +} \ No newline at end of file From 10f4a4cb51503dc965097a2bee292a7bba88fb0f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 20:52:18 +0800 Subject: [PATCH 172/310] Add toString equality test for decorated component Introduced a unit test to verify that the toString() method of the decorated component matches that of its delegate. This ensures consistency between the decorated component and its underlying delegate. --- .../openfeign/components/DecoratedFeignComponentTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index 62b28d20..ced733be 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -113,6 +113,11 @@ void testHashCode() { assertEquals(this.decoratedComponent.hashCode(), this.delegate.hashCode()); } + @Test + void testToString() { + assertEquals(this.decoratedComponent.toString(), this.delegate.toString()); + } + protected abstract T createDelegate(); protected abstract void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass); From 699d787edc5047b06fa6ca1fd56443faad6693eb Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 20:52:43 +0800 Subject: [PATCH 173/310] Add blank lines for readability in test classes Inserted blank lines after class declarations in DecoratedContractTest and DecoratedErrorDecoderTest to improve code readability and maintain consistent formatting. --- .../spring/cloud/openfeign/components/DecoratedContractTest.java | 1 + .../cloud/openfeign/components/DecoratedErrorDecoderTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java index f5381722..550b07eb 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContractTest.java @@ -37,6 +37,7 @@ * @since 1.0.0 */ class DecoratedContractTest extends DecoratedFeignComponentTest { + @Override protected Contract createDelegate() { return new SpringMvcContract(); diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java index 58fac610..0732c8d3 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoderTest.java @@ -34,6 +34,7 @@ * @since 1.0.0 */ class DecoratedErrorDecoderTest extends DecoratedFeignComponentTest { + @Override protected ErrorDecoder createDelegate() { return new Default(); From 02b1423f034d84adb94c2ad0232915d45fd7f65d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 20:54:00 +0800 Subject: [PATCH 174/310] Refactor to use static import for instantiateClass Replaces direct usage of BeanUtils.instantiateClass with a static import of instantiateClass from org.springframework.beans.BeanUtils for improved code clarity. --- .../openfeign/components/CompositedRequestInterceptor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index b768a70e..c0c2ebfe 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -2,7 +2,6 @@ import feign.RequestInterceptor; import feign.RequestTemplate; -import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; @@ -15,6 +14,8 @@ import java.util.Map; import java.util.Set; +import static org.springframework.beans.BeanUtils.instantiateClass; + /** * @author 韩超 * @author Mercy @@ -59,7 +60,7 @@ private RequestInterceptor getInterceptorOrInstantiate(Class Date: Fri, 24 Oct 2025 20:54:04 +0800 Subject: [PATCH 175/310] Refactor to use static import for BeanUtils.instantiateClass Replaces direct usage of BeanUtils.instantiateClass with a static import of instantiateClass for improved readability and consistency. --- .../cloud/openfeign/components/DecoratedFeignComponent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index e64541b0..bbde38b2 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -2,7 +2,6 @@ import io.microsphere.logging.Logger; import org.springframework.beans.BeanInstantiationException; -import org.springframework.beans.BeanUtils; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; @@ -16,6 +15,7 @@ import java.util.function.Function; import static io.microsphere.logging.LoggerFactory.getLogger; +import static org.springframework.beans.BeanUtils.instantiateClass; /** * @author 韩超 @@ -114,7 +114,7 @@ protected T loadInstance() { this.delegate = component; return component; } catch (Throwable ex) { - this.delegate = BeanUtils.instantiateClass(componentType); + this.delegate = instantiateClass(componentType); return delegate; } finally { writeLock.unlock(); @@ -140,7 +140,7 @@ public static , T> W instantiate(Class d String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { try { Constructor constructor = decoratedClass.getConstructor(String.class, NamedContextFactory.class, FeignClientProperties.class, componentClass); - return BeanUtils.instantiateClass(constructor, contextId, contextFactory, clientProperties, delegate); + return instantiateClass(constructor, contextId, contextFactory, clientProperties, delegate); } catch (NoSuchMethodException noSuchMethodException) { throw new BeanInstantiationException(decoratedClass, noSuchMethodException.getLocalizedMessage()); } From 66556e1f6b381a8caa29a88dd7bcfc53d887599b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 20:55:17 +0800 Subject: [PATCH 176/310] Remove commented-out feignClientFactory field Cleaned up the DecoratedFeignComponent class by deleting an unused, commented-out field declaration. --- .../cloud/openfeign/components/DecoratedFeignComponent.java | 1 - 1 file changed, 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index bbde38b2..5ffa1498 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -26,7 +26,6 @@ public abstract class DecoratedFeignComponent implements Refreshable { protected final Logger logger = getLogger(getClass()); - //private final FeignClientFactory feignClientFactory; private final NamedContextFactory contextFactory; private final String contextId; From a3a6b0606ba0e22d33caa42bde30a7f24e3499a0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 21:03:42 +0800 Subject: [PATCH 177/310] Remove unused logger from DecoratedQueryMapEncoder Eliminated the unused Logger and related imports from DecoratedQueryMapEncoder to clean up the code. --- .../openfeign/components/DecoratedQueryMapEncoder.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index 51902323..9604a587 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -1,7 +1,6 @@ package io.microsphere.spring.cloud.openfeign.components; import feign.QueryMapEncoder; -import io.microsphere.logging.Logger; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; @@ -13,7 +12,6 @@ import static io.microsphere.invoke.MethodHandleUtils.findVirtual; import static io.microsphere.invoke.MethodHandleUtils.handleInvokeExactFailure; import static io.microsphere.invoke.MethodHandlesLookupUtils.NOT_FOUND_METHOD_HANDLE; -import static io.microsphere.logging.LoggerFactory.getLogger; /** * @author 韩超 @@ -21,9 +19,7 @@ * @since 0.0.1 */ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent implements QueryMapEncoder { - - private static final Logger logger = getLogger(DecoratedQueryMapEncoder.class); - + private static final String getQueryMapEncoderMethodName = "getQueryMapEncoder"; static final MethodHandle getQueryMapEncoderMethodHandle = findVirtual(FeignClientConfiguration.class, getQueryMapEncoderMethodName); From 56e8f6bcaa5c63225362fb63e6d8083c5ca73dac Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 21:04:52 +0800 Subject: [PATCH 178/310] Fix whitespace in DecoratedQueryMapEncoder Removed unnecessary trailing whitespace in the DecoratedQueryMapEncoder class for code style consistency. --- .../cloud/openfeign/components/DecoratedQueryMapEncoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index 9604a587..e489786c 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -19,7 +19,7 @@ * @since 0.0.1 */ public class DecoratedQueryMapEncoder extends DecoratedFeignComponent implements QueryMapEncoder { - + private static final String getQueryMapEncoderMethodName = "getQueryMapEncoder"; static final MethodHandle getQueryMapEncoderMethodHandle = findVirtual(FeignClientConfiguration.class, getQueryMapEncoderMethodName); From b1d921a63e0161f8f1c9624eda1ece873edb3083 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 24 Oct 2025 21:04:57 +0800 Subject: [PATCH 179/310] Remove unused imports and methods from test class Cleaned up DecoratedFeignComponentTest by removing unused imports and the applicationContextInitializers method. Updated the contextFactory initialization to no longer require initializers. --- .../components/DecoratedFeignComponentTest.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index ced733be..033755a3 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -27,8 +27,6 @@ import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.ResolvableType; import java.util.Map; @@ -36,7 +34,6 @@ import static feign.Request.HttpMethod.GET; import static feign.Request.create; import static feign.Response.builder; -import static io.microsphere.collection.MapUtils.newHashMap; import static io.microsphere.spring.cloud.openfeign.components.DecoratedFeignComponent.instantiate; import static io.microsphere.util.ArrayUtils.EMPTY_BYTE_ARRAY; import static java.nio.charset.StandardCharsets.UTF_8; @@ -71,7 +68,7 @@ abstract class DecoratedFeignComponentTest getDelegateClass() { return (Class) this.delegate.getClass(); } - protected Map> applicationContextInitializers() { - return newHashMap(); - } - protected Request createTestRequest() { return create(GET, "http://localhost", emptyMap(), EMPTY_BYTE_ARRAY, UTF_8, new RequestTemplate()); } From c8c58b4168bec0524d098262e49d8cf1f901677d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 12:18:39 +0800 Subject: [PATCH 180/310] Refactor FeignComponentRegistry interceptor map naming Renamed 'interceptorMap' to 'interceptorsMap' for consistency and clarity. Updated all references accordingly and optimized import usage for NoOpRequestInterceptor. --- .../autorefresh/FeignComponentRegistry.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java index ec97a928..d9a20dc0 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java @@ -8,7 +8,6 @@ import feign.codec.Encoder; import feign.codec.ErrorDecoder; import io.microsphere.spring.cloud.openfeign.components.CompositedRequestInterceptor; -import io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor; import io.microsphere.spring.cloud.openfeign.components.Refreshable; import org.springframework.beans.factory.BeanFactory; import org.springframework.util.ObjectUtils; @@ -21,6 +20,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import static io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor.INSTANCE; + /** * @author 韩超 * @since 0.0.1 @@ -30,7 +31,8 @@ public class FeignComponentRegistry { private static final Map> configComponentMappings = new HashMap<>(16); private final Map> refreshableComponents = new ConcurrentHashMap<>(32); - private final Map interceptorMap = new ConcurrentHashMap<>(32); + + private final Map interceptorsMap = new ConcurrentHashMap<>(32); private final String DEFAULT_CLIENT_NAME; private final BeanFactory beanFactory; @@ -86,10 +88,11 @@ public void register(String clientName, Refreshable component) { } public RequestInterceptor registerRequestInterceptor(String clientName, RequestInterceptor requestInterceptor) { - CompositedRequestInterceptor compositedRequestInterceptor = this.interceptorMap.computeIfAbsent(clientName, (name) -> new CompositedRequestInterceptor(clientName, beanFactory)); + CompositedRequestInterceptor compositedRequestInterceptor = this.interceptorsMap.computeIfAbsent(clientName, (name) -> new CompositedRequestInterceptor(clientName, beanFactory)); if (compositedRequestInterceptor.addRequestInterceptor(requestInterceptor)) { return compositedRequestInterceptor; - } else return NoOpRequestInterceptor.INSTANCE; + } + return INSTANCE; } @@ -119,8 +122,8 @@ public synchronized void refresh(String clientName, Set changedConfig) { }) .forEach(Refreshable::refresh); if (hasInterceptor) - this.interceptorMap.values() - .forEach(CompositedRequestInterceptor::refresh); + this.interceptorsMap.values() + .forEach(CompositedRequestInterceptor::refresh); return; } List components = this.refreshableComponents.get(clientName); @@ -136,7 +139,7 @@ public synchronized void refresh(String clientName, Set changedConfig) { .forEach(Refreshable::refresh); if (hasInterceptor) { - CompositedRequestInterceptor requestInterceptor = this.interceptorMap.get(clientName); + CompositedRequestInterceptor requestInterceptor = this.interceptorsMap.get(clientName); if (requestInterceptor != null) requestInterceptor.refresh(); } From fd14885551d7203f5d1c30edb392205fde5c7557 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 12:18:48 +0800 Subject: [PATCH 181/310] Refactor CompositedRequestInterceptor for clarity and reuse Replaces HashSet with LinkedHashSet to preserve order, introduces utility methods for adding and merging collections, and improves bean instantiation logic using ObjectProvider. Also replaces Spring's CollectionUtils with custom utility methods and simplifies configuration merging logic for headers and parameters. --- .../CompositedRequestInterceptor.java | 81 ++++++++++--------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index c0c2ebfe..1ec78f7a 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -3,17 +3,21 @@ import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; -import org.springframework.util.CollectionUtils; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; +import static io.microsphere.collection.CollectionUtils.isNotEmpty; +import static io.microsphere.collection.MapUtils.isNotEmpty; +import static java.util.Collections.unmodifiableSet; import static org.springframework.beans.BeanUtils.instantiateClass; /** @@ -27,7 +31,7 @@ public class CompositedRequestInterceptor implements RequestInterceptor, Refresh private final String contextId; - private final Set set = new HashSet<>(); + private final Set set = new LinkedHashSet<>(); public CompositedRequestInterceptor(String contextId, BeanFactory beanFactory) { this.beanFactory = beanFactory; @@ -35,16 +39,14 @@ public CompositedRequestInterceptor(String contextId, BeanFactory beanFactory) { } public Set getRequestInterceptors() { - return Collections.unmodifiableSet(set); + return unmodifiableSet(set); } @Override public void apply(RequestTemplate template) { synchronized (this.set) { - if (!this.set.isEmpty()) - set.forEach(requestInterceptor -> requestInterceptor.apply(template)); + set.forEach(requestInterceptor -> requestInterceptor.apply(template)); } - } public boolean addRequestInterceptor(RequestInterceptor requestInterceptor) { @@ -53,54 +55,40 @@ public boolean addRequestInterceptor(RequestInterceptor requestInterceptor) { this.set.add(requestInterceptor); return isFirst; } - } private RequestInterceptor getInterceptorOrInstantiate(Class clazz) { - try { - return this.beanFactory.getBean(clazz); - } catch (Exception e) { - return instantiateClass(clazz); - } + return getOrInstantiate(clazz); } @Override public void refresh() { - FeignClientProperties properties = this.beanFactory.getBean(FeignClientProperties.class); + FeignClientProperties properties = getOrInstantiate(FeignClientProperties.class); Set> interceptors = new HashSet<>(); //headers Map> headers = new HashMap<>(); Map> params = new HashMap<>(); - if (properties != null) { - FeignClientConfiguration defaultConfiguration = properties.getConfig().get(properties.getDefaultConfig()); - FeignClientConfiguration current = properties.getConfig().get(contextId); - if (defaultConfiguration != null && defaultConfiguration.getRequestInterceptors() != null) - interceptors.addAll(defaultConfiguration.getRequestInterceptors()); - if (current != null && current.getRequestInterceptors() != null) - interceptors.addAll(current.getRequestInterceptors()); - - if (defaultConfiguration != null && defaultConfiguration.getDefaultRequestHeaders() != null) - headers.putAll(defaultConfiguration.getDefaultRequestHeaders()); - - if (current != null && current.getDefaultRequestHeaders() != null) { - current.getDefaultRequestHeaders().forEach(headers::putIfAbsent); - } - if (defaultConfiguration != null && defaultConfiguration.getDefaultQueryParameters() != null) - params.putAll(defaultConfiguration.getDefaultRequestHeaders()); + Map config = properties.getConfig(); + FeignClientConfiguration defaultConfiguration = config.get(properties.getDefaultConfig()); + FeignClientConfiguration currentConfiguration = config.get(contextId); - if (current != null && current.getDefaultQueryParameters() != null) { - current.getDefaultQueryParameters().forEach(params::putIfAbsent); - } + addAll(defaultConfiguration::getRequestInterceptors, interceptors); + addAll(currentConfiguration::getRequestInterceptors, interceptors); - } + putIfAbsent(defaultConfiguration::getDefaultRequestHeaders, headers); + putIfAbsent(currentConfiguration::getDefaultRequestHeaders, headers); + + putIfAbsent(defaultConfiguration::getDefaultQueryParameters, params); + putIfAbsent(currentConfiguration::getDefaultQueryParameters, params); synchronized (this.set) { this.set.clear(); - for (Class interceptorClass : interceptors) + for (Class interceptorClass : interceptors) { set.add(getInterceptorOrInstantiate(interceptorClass)); + } - if (!CollectionUtils.isEmpty(headers)) + if (isNotEmpty(headers)) set.add(requestTemplate -> { Map> requestHeader = requestTemplate.headers(); headers.keySet().forEach(key -> { @@ -110,7 +98,7 @@ public void refresh() { }); }); - if (!CollectionUtils.isEmpty(params)) + if (isNotEmpty(params)) set.add(requestTemplate -> { Map> requestQueries = requestTemplate.queries(); params.keySet().forEach(key -> { @@ -121,4 +109,23 @@ public void refresh() { }); } } + + static void addAll(Supplier> sourceSupplier, Collection target) { + Collection source = sourceSupplier.get(); + if (isNotEmpty(source)) { + source.forEach(target::add); + } + } + + T getOrInstantiate(Class clazz) { + ObjectProvider beanProvider = beanFactory.getBeanProvider(clazz); + return beanProvider.getIfAvailable(() -> instantiateClass(clazz)); + } + + static void putIfAbsent(Supplier> sourceSupplier, Map target) { + Map source = sourceSupplier.get(); + if (isNotEmpty(source)) { + source.forEach(target::putIfAbsent); + } + } } \ No newline at end of file From 4c1ca1b84459ae437631b1b0729683ce0cfa079c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 12:18:54 +0800 Subject: [PATCH 182/310] Remove private constructor from NoOpRequestInterceptor The private constructor in NoOpRequestInterceptor was removed, making the default constructor accessible. This change may improve testability or instantiation flexibility. --- .../cloud/openfeign/components/NoOpRequestInterceptor.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java index 1bc004c7..071c5395 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java @@ -12,9 +12,6 @@ public class NoOpRequestInterceptor implements RequestInterceptor { public static final NoOpRequestInterceptor INSTANCE = new NoOpRequestInterceptor(); - private NoOpRequestInterceptor() { - } - @Override public void apply(RequestTemplate template) { //no op From 57e608103af408d7be2f434792a157a24215de06 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 12:18:58 +0800 Subject: [PATCH 183/310] Add unit tests for CompositedRequestInterceptor Introduces CompositedRequestInterceptorTest to verify the behavior of CompositedRequestInterceptor, including adding and applying request interceptors, refreshing configuration, and handling default headers and query parameters. --- .../CompositedRequestInterceptorTest.java | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptorTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptorTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptorTest.java new file mode 100644 index 00000000..57eb2754 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptorTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.openfeign.components; + + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor.INSTANCE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link CompositedRequestInterceptor} Test + * + * @author Mercy + * @see CompositedRequestInterceptor + * @since 1.0.0 + */ +class CompositedRequestInterceptorTest { + + private static final String TEST_NAME = "test-name"; + + private static final String TEST_VALUE = "test-value"; + + protected String contextId; + + private FeignClientProperties feignClientProperties; + + private GenericApplicationContext context; + + private CompositedRequestInterceptor interceptor; + + @BeforeEach + void setUp() { + this.contextId = "test-context"; + this.feignClientProperties = new FeignClientProperties(); + this.context = new GenericApplicationContext(); + this.context.setId(contextId); + this.context.registerBean(FeignClientProperties.class, () -> feignClientProperties); + this.context.refresh(); + this.interceptor = new CompositedRequestInterceptor(this.contextId, this.context); + initFeignClientProperties(); + } + + void initFeignClientProperties() { + Map config = this.feignClientProperties.getConfig(); + config.put(this.contextId, new FeignClientConfiguration()); + config.put(this.feignClientProperties.getDefaultConfig(), new FeignClientConfiguration()); + } + + FeignClientConfiguration getDefaultConfiguration() { + return this.feignClientProperties.getConfig().get(this.feignClientProperties.getDefaultConfig()); + } + + FeignClientConfiguration getCurrentConfiguration() { + return this.feignClientProperties.getConfig().get(this.contextId); + } + + @Test + void testGetRequestInterceptors() { + assertTrue(this.interceptor.getRequestInterceptors().isEmpty()); + } + + @Test + void testAddRequestInterceptor() { + this.interceptor.addRequestInterceptor(INSTANCE); + assertEquals(1, this.interceptor.getRequestInterceptors().size()); + assertTrue(this.interceptor.getRequestInterceptors().contains(INSTANCE)); + } + + @Test + void testApply() { + RequestTemplate template = new RequestTemplate(); + this.interceptor.apply(template); + this.interceptor.addRequestInterceptor(INSTANCE); + this.interceptor.apply(template); + } + + @Test + void testRefresh() { + this.interceptor.refresh(); + } + + @Test + void testRefreshOnDefaultConfiguration() { + FeignClientConfiguration defaultConfiguration = getDefaultConfiguration(); + testRefresh(defaultConfiguration); + } + + @Test + void testRefreshOnCurrentConfiguration() { + FeignClientConfiguration currentConfiguration = getCurrentConfiguration(); + addRequestInterceptor(currentConfiguration, TestRequestInterceptor.class); + testRefresh(currentConfiguration); + } + + void testRefresh(FeignClientConfiguration configuration) { + initFeignClientConfiguration(configuration); + this.interceptor.refresh(); + + RequestTemplate template = new RequestTemplate(); + this.interceptor.apply(template); + } + + void initFeignClientConfiguration(FeignClientConfiguration configuration) { + addRequestInterceptor(configuration, NoOpRequestInterceptor.class); + + Map> headers = configuration.getDefaultRequestHeaders(); + add(headers, TEST_NAME, TEST_VALUE); + + Map> parameters = configuration.getDefaultQueryParameters(); + add(parameters, TEST_NAME, TEST_VALUE); + } + + void addRequestInterceptor(FeignClientConfiguration configuration, Class requestInterceptorClass) { + List> requestInterceptors = configuration.getRequestInterceptors(); + if (requestInterceptors == null) { + requestInterceptors = new ArrayList<>(); + configuration.setRequestInterceptors(requestInterceptors); + } + requestInterceptors.add((Class) requestInterceptorClass); + } + + void add(Map> map, String name, String value) { + Collection values = map.computeIfAbsent(name, n -> new ArrayList<>()); + values.add(value); + } + + static class TestRequestInterceptor implements RequestInterceptor { + + @Override + public void apply(RequestTemplate template) { + template.query(TEST_NAME, TEST_VALUE); + template.header(TEST_NAME, TEST_VALUE); + } + } +} \ No newline at end of file From 960a3d8e56bb9b032b6bdacc574e70905aa80c83 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 16:46:37 +0800 Subject: [PATCH 184/310] Refactor to use FeignClientSpecification directly Replaces reflection-based access to FeignClientSpecification with direct usage of the class and its methods. Simplifies injectAutoRefreshCapability by removing reflection and improves logging with arrayToString. --- ...FeignClientSpecificationPostProcessor.java | 36 +++++-------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java index 52eb071b..8ea88194 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java @@ -4,16 +4,12 @@ import io.microsphere.spring.cloud.openfeign.autorefresh.AutoRefreshCapability; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.cloud.context.named.NamedContextFactory; - -import java.lang.reflect.Method; -import java.util.Arrays; +import org.springframework.cloud.openfeign.FeignClientSpecification; import static io.microsphere.logging.LoggerFactory.getLogger; -import static io.microsphere.reflect.MethodUtils.findMethod; +import static io.microsphere.util.ArrayUtils.arrayToString; import static io.microsphere.util.ArrayUtils.combine; import static org.springframework.aop.support.AopUtils.getTargetClass; -import static org.springframework.util.ClassUtils.resolveClassName; /** * @author 韩超 @@ -27,35 +23,21 @@ public class FeignClientSpecificationPostProcessor implements BeanPostProcessor private static final Class AUTO_REFRESH_CAPABILITY_CLASS = AutoRefreshCapability.class; - private static final String FEIGN_CLIENT_SPECIFICATION_CLASS_NAME = "org.springframework.cloud.openfeign.FeignClientSpecification"; - - private static final Class FEIGN_CLIENT_SPECIFICATION_CLASS = resolveClassName(FEIGN_CLIENT_SPECIFICATION_CLASS_NAME, null); - - private static final Method setConfigurationMethod = findMethod(FEIGN_CLIENT_SPECIFICATION_CLASS, "setConfiguration", Class[].class); + private static final Class FEIGN_CLIENT_SPECIFICATION_CLASS = FeignClientSpecification.class; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class beanType = getTargetClass(bean); if (FEIGN_CLIENT_SPECIFICATION_CLASS.isAssignableFrom(beanType) && beanName.startsWith("default")) { - injectAutoRefreshCapability((NamedContextFactory.Specification) bean); + injectAutoRefreshCapability((FeignClientSpecification) bean); } return bean; } - private void injectAutoRefreshCapability(NamedContextFactory.Specification defaultSpecification) { - if (setConfigurationMethod != null) { - Class[] originConfigurationClasses = defaultSpecification.getConfiguration(); - Class[] newConfigurationClasses = combine(AUTO_REFRESH_CAPABILITY_CLASS, originConfigurationClasses); - Object arg = newConfigurationClasses; - try { - setConfigurationMethod.setAccessible(true); - setConfigurationMethod.invoke(defaultSpecification, arg); - } catch (Throwable e) { - if (logger.isWarnEnabled()) { - logger.warn("FeignClientSpecification#setConfiguration(Class[]) can't be invoked , instance : {} , args : {}", - defaultSpecification, Arrays.toString(newConfigurationClasses)); - } - } - } + private void injectAutoRefreshCapability(FeignClientSpecification specification) { + Class[] originConfigurationClasses = specification.getConfiguration(); + Class[] newConfigurationClasses = combine(AUTO_REFRESH_CAPABILITY_CLASS, originConfigurationClasses); + specification.setConfiguration(newConfigurationClasses); + logger.trace("The Configuration classes: before - {} , after - {}", arrayToString(originConfigurationClasses), arrayToString(newConfigurationClasses)); } } From 6cb8199deb5c69865250cce747d73e1bf02c80b7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 16:59:14 +0800 Subject: [PATCH 185/310] Add class-level Javadoc to FeignClientSpecificationPostProcessor Introduced a Javadoc comment describing the purpose of the FeignClientSpecificationPostProcessor class, clarifying its role as a BeanPostProcessor for FeignClientSpecification. --- .../FeignClientSpecificationPostProcessor.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java index 8ea88194..1dd44c8d 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java @@ -12,6 +12,8 @@ import static org.springframework.aop.support.AopUtils.getTargetClass; /** + * {@link BeanPostProcessor} for {@link FeignClientSpecification} + * * @author 韩超 * @author Mercy * @see org.springframework.cloud.openfeign.FeignClientSpecification @@ -38,6 +40,7 @@ private void injectAutoRefreshCapability(FeignClientSpecification specification) Class[] originConfigurationClasses = specification.getConfiguration(); Class[] newConfigurationClasses = combine(AUTO_REFRESH_CAPABILITY_CLASS, originConfigurationClasses); specification.setConfiguration(newConfigurationClasses); - logger.trace("The Configuration classes: before - {} , after - {}", arrayToString(originConfigurationClasses), arrayToString(newConfigurationClasses)); + logger.trace("The Configuration classes: before - {} , after - {}", arrayToString(originConfigurationClasses), + arrayToString(newConfigurationClasses)); } -} +} \ No newline at end of file From 71ab129c82fe770159f1d1c46dad2fa33fc6344b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:18:35 +0800 Subject: [PATCH 186/310] Use explicit beanFactory reference in getOrInstantiate Replaces direct access to beanFactory with 'this.beanFactory' in getOrInstantiate for clarity and consistency. --- .../openfeign/components/CompositedRequestInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index 1ec78f7a..81f65d74 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -118,7 +118,7 @@ static void addAll(Supplier> sourceSupplier, Collection tar } T getOrInstantiate(Class clazz) { - ObjectProvider beanProvider = beanFactory.getBeanProvider(clazz); + ObjectProvider beanProvider = this.beanFactory.getBeanProvider(clazz); return beanProvider.getIfAvailable(() -> instantiateClass(clazz)); } From 40f76f522fd8b9f3c50c555751fba78c617b6877 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:18:41 +0800 Subject: [PATCH 187/310] Refactor Feign component instantiation logic Replaces direct context factory instance retrieval with ObjectProvider to streamline bean instantiation and fallback. Simplifies error handling and ensures consistent delegate assignment in DecoratedFeignComponent. --- .../components/DecoratedFeignComponent.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 5ffa1498..6fb676c9 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -2,6 +2,7 @@ import io.microsphere.logging.Logger; import org.springframework.beans.BeanInstantiationException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; @@ -61,10 +62,8 @@ public T delegate() { @NonNull public T loadInstanceFromContextFactory(String contextId, Class componentType) { - T component = this.contextFactory.getInstance(contextId, componentType); - if (component == null) - return this.contextFactory.getParent().getAutowireCapableBeanFactory().createBean(componentType); - return component; + ObjectProvider beanProvider = this.contextFactory.getProvider(contextId, componentType); + return beanProvider.getIfAvailable(() -> instantiateClass(componentType)); } @NonNull @@ -107,17 +106,15 @@ protected T get(Function configurationFunction) protected T loadInstance() { Class componentType = componentType(); String contextId = contextId(); + T bean = null; writeLock.lock(); try { - T component = loadInstanceFromContextFactory(contextId, componentType); - this.delegate = component; - return component; - } catch (Throwable ex) { - this.delegate = instantiateClass(componentType); - return delegate; + bean = loadInstanceFromContextFactory(contextId, componentType); } finally { + this.delegate = bean; writeLock.unlock(); } + return bean; } @Override @@ -143,6 +140,5 @@ public static , T> W instantiate(Class d } catch (NoSuchMethodException noSuchMethodException) { throw new BeanInstantiationException(decoratedClass, noSuchMethodException.getLocalizedMessage()); } - } } \ No newline at end of file From c7c49f2bfc3b35bf4214928e426264436f0adc4a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:18:46 +0800 Subject: [PATCH 188/310] Refactor generics in DecoratedFeignComponentTest Changed generic type parameter from T to C for clarity and consistency. Updated method signatures and variable types accordingly. Added a new test method to verify loadInstanceFromContextFactory returns non-null instances. --- .../DecoratedFeignComponentTest.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index 033755a3..055f9bdb 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -39,6 +39,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.springframework.core.ResolvableType.forClass; @@ -49,7 +50,7 @@ * @see DecoratedFeignComponent * @since 1.0.0 */ -abstract class DecoratedFeignComponentTest> { +abstract class DecoratedFeignComponentTest> { protected String contextId; @@ -57,11 +58,11 @@ abstract class DecoratedFeignComponentTest componentClass; + protected Class componentClass; protected Class decoratedComponentClass; - protected T delegate; + protected C delegate; protected D decoratedComponent; @@ -72,7 +73,7 @@ void setUp() { this.clientProperties = new FeignClientProperties(); ResolvableType resolvableType = forClass(this.getClass()).as(DecoratedFeignComponentTest.class); - this.componentClass = (Class) resolvableType.getGeneric(0).resolve(); + this.componentClass = (Class) resolvableType.getGeneric(0).resolve(); this.decoratedComponentClass = (Class) resolvableType.getGeneric(1).resolve(); this.delegate = createDelegate(); @@ -82,7 +83,7 @@ void setUp() { @Test void testComponentTypeFromDefaultConfiguration() { initDefaultConfiguration(); - Class delegateClass = getDelegateClass(); + Class delegateClass = getDelegateClass(); configureDelegateClass(this.decoratedComponent.getDefaultConfiguration(), delegateClass); assertSame(delegateClass, this.decoratedComponent.componentType()); } @@ -90,7 +91,7 @@ void testComponentTypeFromDefaultConfiguration() { @Test void testComponentTypeFromCurrentConfiguration() { initCurrentConfiguration(); - Class delegateClass = getDelegateClass(); + Class delegateClass = getDelegateClass(); configureDelegateClass(this.decoratedComponent.getCurrentConfiguration(), delegateClass); assertSame(delegateClass, this.decoratedComponent.componentType()); } @@ -115,12 +116,20 @@ void testToString() { assertEquals(this.decoratedComponent.toString(), this.delegate.toString()); } - protected abstract T createDelegate(); + @Test + void testLoadInstanceFromContextFactory() { + C component = this.decoratedComponent.loadInstanceFromContextFactory(this.contextId, this.componentClass); + assertNotNull(component); + + assertNotNull(this.decoratedComponent.loadInstanceFromContextFactory(this.contextId, String.class)); + } + + protected abstract C createDelegate(); - protected abstract void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass); + protected abstract void configureDelegateClass(FeignClientConfiguration configuration, Class delegateClass); - protected Class getDelegateClass() { - return (Class) this.delegate.getClass(); + protected Class getDelegateClass() { + return (Class) this.delegate.getClass(); } protected Request createTestRequest() { From b84ebb2f7e3cbcfa6181bc8371599ed43aaf117c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:21:04 +0800 Subject: [PATCH 189/310] Refactor instantiate method to use findConstructor Replaces manual constructor lookup and exception handling in the instantiate method with the use of findConstructor utility. This simplifies the code and removes dependency on BeanInstantiationException. --- .../components/DecoratedFeignComponent.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 6fb676c9..91653af9 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -1,7 +1,6 @@ package io.microsphere.spring.cloud.openfeign.components; import io.microsphere.logging.Logger; -import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.openfeign.FeignClientProperties; @@ -16,6 +15,7 @@ import java.util.function.Function; import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.reflect.ConstructorUtils.findConstructor; import static org.springframework.beans.BeanUtils.instantiateClass; /** @@ -132,13 +132,13 @@ public String toString() { return delegate().toString(); } - public static , T> W instantiate(Class decoratedClass, Class componentClass, - String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { - try { - Constructor constructor = decoratedClass.getConstructor(String.class, NamedContextFactory.class, FeignClientProperties.class, componentClass); - return instantiateClass(constructor, contextId, contextFactory, clientProperties, delegate); - } catch (NoSuchMethodException noSuchMethodException) { - throw new BeanInstantiationException(decoratedClass, noSuchMethodException.getLocalizedMessage()); - } + public static , T> W instantiate(Class decoratedClass, + Class componentClass, + String contextId, + NamedContextFactory contextFactory, + FeignClientProperties clientProperties, + T delegate) { + Constructor constructor = findConstructor(decoratedClass, String.class, NamedContextFactory.class, FeignClientProperties.class, componentClass); + return instantiateClass(constructor, contextId, contextFactory, clientProperties, delegate); } } \ No newline at end of file From e0f9862b1d42a5d6ed0355a491c923a7863f95df Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:33:29 +0800 Subject: [PATCH 190/310] Refactor DecoratedFeignComponentTest assertions Replaced assertSame with assertTrue and isAssignableFrom in testComponentType for more flexible type checking. Refactored testLoadInstanceFromContextFactory to use local variables for contextId and componentType, improving clarity. --- .../components/DecoratedFeignComponentTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index 055f9bdb..cef8b049 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -41,6 +41,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.core.ResolvableType.forClass; /** @@ -98,7 +99,7 @@ void testComponentTypeFromCurrentConfiguration() { @Test void testComponentType() { - assertSame(componentClass, this.decoratedComponent.componentType()); + assertTrue(componentClass.isAssignableFrom(this.decoratedComponent.componentType())); } @Test @@ -118,10 +119,12 @@ void testToString() { @Test void testLoadInstanceFromContextFactory() { - C component = this.decoratedComponent.loadInstanceFromContextFactory(this.contextId, this.componentClass); + String contextId = this.decoratedComponent.contextId(); + Class componentType = this.decoratedComponent.componentType(); + C component = this.decoratedComponent.loadInstanceFromContextFactory(contextId, componentType); assertNotNull(component); - assertNotNull(this.decoratedComponent.loadInstanceFromContextFactory(this.contextId, String.class)); + assertNotNull(this.decoratedComponent.loadInstanceFromContextFactory(contextId, String.class)); } protected abstract C createDelegate(); From 8eac9deff1d0debbfb0e0931d73213c444df4204 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:33:34 +0800 Subject: [PATCH 191/310] Fix default ErrorDecoder class in DecoratedErrorDecoder Updated the componentType() method to return Default.class instead of ErrorDecoder.class when no custom ErrorDecoder is configured. This ensures the correct default implementation is used. --- .../cloud/openfeign/components/DecoratedErrorDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 1f866aae..791421d6 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -21,7 +21,7 @@ public DecoratedErrorDecoder(String contextId, NamedContextFactory componentType() { Class errorDecoderClass = get(FeignClientConfiguration::getErrorDecoder); - return errorDecoderClass == null ? ErrorDecoder.class : errorDecoderClass; + return errorDecoderClass == null ? (Class) Default.class : errorDecoderClass; } @Override From f34e5204cdf4f3a824d13eddc8ca2f4eda460021 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:33:37 +0800 Subject: [PATCH 192/310] Use PageableSpringQueryMapEncoder as default encoder Updated DecoratedQueryMapEncoder to use PageableSpringQueryMapEncoder as the default QueryMapEncoder when no specific encoder is configured. This change ensures better support for Spring's Pageable types in Feign clients. --- .../cloud/openfeign/components/DecoratedQueryMapEncoder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index e489786c..4b23de31 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -5,6 +5,7 @@ import org.springframework.cloud.openfeign.FeignClientProperties; import org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClientSpecification; +import org.springframework.cloud.openfeign.support.PageableSpringQueryMapEncoder; import java.lang.invoke.MethodHandle; import java.util.Map; @@ -34,7 +35,7 @@ protected Class componentType() { if (queryMapEncoderClass == null) { queryMapEncoderClass = getQueryMapEncoder(getDefaultConfiguration()); } - return queryMapEncoderClass == null ? QueryMapEncoder.class : queryMapEncoderClass; + return queryMapEncoderClass == null ? (Class) PageableSpringQueryMapEncoder.class : queryMapEncoderClass; } private Class getQueryMapEncoder(FeignClientConfiguration feignClientConfiguration) { From 472db31ca037f3513bd34ba5a0c99b10d10490c4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:34:54 +0800 Subject: [PATCH 193/310] Use bounded wildcard for componentType return type Changed the return type of the protected abstract method componentType() from Class to Class in DecoratedFeignComponent and its subclasses. This improves type safety and flexibility when dealing with subclasses of the component type. --- .../spring/cloud/openfeign/components/DecoratedContract.java | 2 +- .../spring/cloud/openfeign/components/DecoratedDecoder.java | 2 +- .../spring/cloud/openfeign/components/DecoratedEncoder.java | 2 +- .../cloud/openfeign/components/DecoratedErrorDecoder.java | 4 ++-- .../cloud/openfeign/components/DecoratedFeignComponent.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index cedef798..490fd88a 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -21,7 +21,7 @@ public DecoratedContract(String contextId, NamedContextFactory componentType() { + protected Class componentType() { Class contractClass = get(FeignClientConfiguration::getContract); return contractClass == null ? Contract.class : contractClass; } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index e5e495fd..9d3cb79c 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -24,7 +24,7 @@ public DecoratedDecoder(String contextId, NamedContextFactory componentType() { + protected Class componentType() { Class decoderClass = get(FeignClientConfiguration::getDecoder); return decoderClass == null ? Decoder.class : decoderClass; } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index 69d62d76..f8ae900c 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -23,7 +23,7 @@ public DecoratedEncoder(String contextId, NamedContextFactory componentType() { + protected Class componentType() { Class encoderClass = get(FeignClientConfiguration::getEncoder); return encoderClass == null ? Encoder.class : encoderClass; } diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 791421d6..1670573e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -19,9 +19,9 @@ public DecoratedErrorDecoder(String contextId, NamedContextFactory componentType() { + protected Class componentType() { Class errorDecoderClass = get(FeignClientConfiguration::getErrorDecoder); - return errorDecoderClass == null ? (Class) Default.class : errorDecoderClass; + return errorDecoderClass == null ? Default.class : errorDecoderClass; } @Override diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 91653af9..44358e32 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -78,7 +78,7 @@ public void refresh() { writeLock.unlock(); } - protected abstract Class componentType(); + protected abstract Class componentType(); public FeignClientConfiguration getDefaultConfiguration() { return this.clientProperties.getConfig().get(this.clientProperties.getDefaultConfig()); From 9f501b2c767c8ffa03328908e99f0cbfa1b3013d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 17:34:59 +0800 Subject: [PATCH 194/310] Refine componentType return types in Feign components Updated the componentType method signatures in DecoratedQueryMapEncoder and DecoratedRetryer to return Class instead of Class for improved type safety and clarity. --- .../cloud/openfeign/components/DecoratedQueryMapEncoder.java | 4 ++-- .../spring/cloud/openfeign/components/DecoratedRetryer.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index 4b23de31..fcfddc41 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -30,12 +30,12 @@ public DecoratedQueryMapEncoder(String contextId, NamedContextFactory componentType() { + protected Class componentType() { Class queryMapEncoderClass = getQueryMapEncoder(getCurrentConfiguration()); if (queryMapEncoderClass == null) { queryMapEncoderClass = getQueryMapEncoder(getDefaultConfiguration()); } - return queryMapEncoderClass == null ? (Class) PageableSpringQueryMapEncoder.class : queryMapEncoderClass; + return queryMapEncoderClass == null ? PageableSpringQueryMapEncoder.class : queryMapEncoderClass; } private Class getQueryMapEncoder(FeignClientConfiguration feignClientConfiguration) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java index 0a668331..f4dc74fe 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java @@ -19,7 +19,7 @@ public DecoratedRetryer(String contextId, NamedContextFactory componentType() { + protected Class componentType() { Class retryerClass = get(FeignClientConfiguration::getRetryer); return retryerClass == null ? Retryer.class : retryerClass; } From 267a5807461888fa74890bb52bc5c52fc9d6b3a8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 18:20:29 +0800 Subject: [PATCH 195/310] Update type of componentType in loadInstance method Changed the type of the componentType variable in the loadInstance method from Class to Class to allow for subclasses of T. This improves type flexibility when loading component instances. --- .../cloud/openfeign/components/DecoratedFeignComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 44358e32..62d5bee4 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -104,7 +104,7 @@ protected T get(Function configurationFunction) } protected T loadInstance() { - Class componentType = componentType(); + Class componentType = componentType(); String contextId = contextId(); T bean = null; writeLock.lock(); From 3874134e1758050a34e0a9ddf6287fce88b7d48f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 18:26:05 +0800 Subject: [PATCH 196/310] Update DecoratedFeignComponentTest.java --- .../cloud/openfeign/components/DecoratedFeignComponentTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java index cef8b049..1efd73a4 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java @@ -120,7 +120,7 @@ void testToString() { @Test void testLoadInstanceFromContextFactory() { String contextId = this.decoratedComponent.contextId(); - Class componentType = this.decoratedComponent.componentType(); + Class componentType = this.decoratedComponent.componentType(); C component = this.decoratedComponent.loadInstanceFromContextFactory(contextId, componentType); assertNotNull(component); From ff06818313220ef9fc6713a8ca921570087a678e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 18:55:11 +0800 Subject: [PATCH 197/310] Remove ReentrantReadWriteLock from DecoratedFeignComponent Eliminated the use of ReentrantReadWriteLock and related locking logic from DecoratedFeignComponent. The delegate is now managed without explicit locking, simplifying the code and reducing complexity. --- .../components/DecoratedFeignComponent.java | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index 62d5bee4..c5f4b376 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -9,9 +9,6 @@ import org.springframework.lang.NonNull; import java.lang.reflect.Constructor; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.function.Function; import static io.microsphere.logging.LoggerFactory.getLogger; @@ -35,12 +32,6 @@ public abstract class DecoratedFeignComponent implements Refreshable { protected volatile T delegate; - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - - private final ReadLock readLock = lock.readLock(); - - private final WriteLock writeLock = lock.writeLock(); - public DecoratedFeignComponent(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { this.contextId = contextId; this.contextFactory = contextFactory; @@ -49,15 +40,13 @@ public DecoratedFeignComponent(String contextId, NamedContextFactory componentType(); @@ -106,15 +93,7 @@ protected T get(Function configurationFunction) protected T loadInstance() { Class componentType = componentType(); String contextId = contextId(); - T bean = null; - writeLock.lock(); - try { - bean = loadInstanceFromContextFactory(contextId, componentType); - } finally { - this.delegate = bean; - writeLock.unlock(); - } - return bean; + return loadInstanceFromContextFactory(contextId, componentType); } @Override From ac1855bba804220c59e31d3bcfaf1d548266bd17 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 19:17:52 +0800 Subject: [PATCH 198/310] Add braces to null checks in enrich methods Refactored the enrich methods in AutoRefreshCapability to use braces for all null checks, improving code readability and consistency. --- .../autorefresh/AutoRefreshCapability.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 78235ec1..a4bf400e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -50,23 +50,23 @@ public void setApplicationContext(ApplicationContext applicationContext) throws this.contextId = applicationContext.getEnvironment().getProperty("spring.cloud.openfeign.client.name"); } - @Override public Retryer enrich(Retryer retryer) { - if (retryer == null) + if (retryer == null) { return null; + } DecoratedRetryer decoratedRetryer = instantiate(DecoratedRetryer.class, Retryer.class, contextId, contextFactory, clientProperties, retryer); - this.componentRegistry.register(contextId, decoratedRetryer); return decoratedRetryer; } @Override public Contract enrich(Contract contract) { - if (contract == null) + if (contract == null) { return null; + } DecoratedContract decoratedContract = instantiate(DecoratedContract.class, Contract.class, contextId, contextFactory, clientProperties, contract); @@ -76,8 +76,9 @@ public Contract enrich(Contract contract) { @Override public Decoder enrich(Decoder decoder) { - if (decoder == null) + if (decoder == null) { return null; + } DecoratedDecoder decoratedDecoder = instantiate(DecoratedDecoder.class, Decoder.class, contextId, contextFactory, clientProperties, decoder); @@ -87,8 +88,9 @@ public Decoder enrich(Decoder decoder) { @Override public Encoder enrich(Encoder encoder) { - if (encoder == null) + if (encoder == null) { return null; + } DecoratedEncoder decoratedEncoder = instantiate(DecoratedEncoder.class, Encoder.class, contextId, contextFactory, clientProperties, encoder); @@ -97,8 +99,9 @@ public Encoder enrich(Encoder encoder) { } public ErrorDecoder enrich(ErrorDecoder decoder) { - if (decoder == null) + if (decoder == null) { return null; + } DecoratedErrorDecoder decoratedErrorDecoder = instantiate(DecoratedErrorDecoder.class, ErrorDecoder.class, contextId, contextFactory, clientProperties, decoder); @@ -108,13 +111,17 @@ public ErrorDecoder enrich(ErrorDecoder decoder) { @Override public RequestInterceptor enrich(RequestInterceptor requestInterceptor) { + if (requestInterceptor == null) { + return null; + } return this.componentRegistry.registerRequestInterceptor(contextId, requestInterceptor); } @Override public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { - if (queryMapEncoder == null) + if (queryMapEncoder == null) { return null; + } DecoratedQueryMapEncoder decoratedQueryMapEncoder = instantiate(DecoratedQueryMapEncoder.class, QueryMapEncoder.class, contextId, contextFactory, clientProperties, queryMapEncoder); From e1b29354417b25e164cc46cb8834df1ea510d950 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 19:17:59 +0800 Subject: [PATCH 199/310] Rename DEFAULT_CLIENT_NAME to defaultClientName Refactored the FeignComponentRegistry class to use 'defaultClientName' instead of 'DEFAULT_CLIENT_NAME' for consistency with Java naming conventions for instance variables. --- .../openfeign/autorefresh/FeignComponentRegistry.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java index d9a20dc0..e2cd5112 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java @@ -34,7 +34,8 @@ public class FeignComponentRegistry { private final Map interceptorsMap = new ConcurrentHashMap<>(32); - private final String DEFAULT_CLIENT_NAME; + private final String defaultClientName; + private final BeanFactory beanFactory; static { @@ -73,7 +74,7 @@ protected static Class getComponentClass(String config) { } public FeignComponentRegistry(String defaultClientName, BeanFactory beanFactory) { - this.DEFAULT_CLIENT_NAME = defaultClientName; + this.defaultClientName = defaultClientName; this.beanFactory = beanFactory; } @@ -109,7 +110,7 @@ public synchronized void refresh(String clientName, Set changedConfig) { } } - if (DEFAULT_CLIENT_NAME.equals(clientName)) { + if (defaultClientName.equals(clientName)) { //default configs changed, need refresh all refreshableComponents.values().stream() .flatMap(List::stream) From 7f8a690c9cbcf5fd726d789727234ec5335046a3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 19:18:03 +0800 Subject: [PATCH 200/310] Add unit test for AutoRefreshCapability Introduces AutoRefreshCapabilityTest to verify the enrich method returns null for various Feign component types. Sets up required context and properties for testing. --- .../AutoRefreshCapabilityTest.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapabilityTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapabilityTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapabilityTest.java new file mode 100644 index 00000000..ef575be2 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapabilityTest.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.openfeign.autorefresh; + + +import feign.Contract; +import feign.QueryMapEncoder; +import feign.RequestInterceptor; +import feign.Retryer; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.openfeign.FeignClientFactory; +import org.springframework.cloud.openfeign.FeignClientProperties; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link AutoRefreshCapability} Test + * + * @author Mercy + * @see AutoRefreshCapability + * @since 1.0.0 + */ +class AutoRefreshCapabilityTest { + + private String contextId; + + private FeignClientProperties feignClientProperties; + + private FeignClientFactory feignClientFactory; + + private GenericApplicationContext context; + + private FeignComponentRegistry feignComponentRegistry; + + private AutoRefreshCapability capability; + + @BeforeEach + void setUp() { + this.contextId = "test-context"; + this.feignClientProperties = new FeignClientProperties(); + this.feignClientFactory = new FeignClientFactory(); + this.context = new GenericApplicationContext(); + this.context.setId(contextId); + this.context.registerBean(FeignClientProperties.class, () -> this.feignClientProperties); + this.context.refresh(); + this.feignComponentRegistry = new FeignComponentRegistry(this.contextId, this.context); + this.capability = new AutoRefreshCapability(this.feignClientProperties, this.feignClientFactory, this.feignComponentRegistry); + initFeignClientProperties(); + } + + void initFeignClientProperties() { + Map config = this.feignClientProperties.getConfig(); + config.put(this.contextId, new FeignClientProperties.FeignClientConfiguration()); + config.put(this.feignClientProperties.getDefaultConfig(), new FeignClientProperties.FeignClientConfiguration()); + } + + @Test + void testEnrich() { + assertNull(this.capability.enrich((Retryer) null)); + assertNull(this.capability.enrich((Contract) null)); + assertNull(this.capability.enrich((Decoder) null)); + assertNull(this.capability.enrich((Encoder) null)); + assertNull(this.capability.enrich((ErrorDecoder) null)); + assertNull(this.capability.enrich((RequestInterceptor) null)); + assertNull(this.capability.enrich((QueryMapEncoder) null)); + } +} \ No newline at end of file From a072b296a5a17f89c681532af6db4bc0299b711f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sat, 25 Oct 2025 19:19:10 +0800 Subject: [PATCH 201/310] Update AutoRefreshCapability.java --- .../cloud/openfeign/autorefresh/AutoRefreshCapability.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index a4bf400e..454e29a7 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -129,4 +129,4 @@ public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { this.componentRegistry.register(contextId, decoratedQueryMapEncoder); return decoratedQueryMapEncoder; } -} +} \ No newline at end of file From 5d0c5ab0e7849b409f35f6f7d3987791b6b07559 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 26 Oct 2025 12:09:46 +0800 Subject: [PATCH 202/310] Add Mercy as author to AutoRefreshCapability Updated the AutoRefreshCapability.java file to include Mercy as an author in the class-level Javadoc. --- .../cloud/openfeign/autorefresh/AutoRefreshCapability.java | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index 454e29a7..ebc5b9f2 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -25,6 +25,7 @@ /** * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class AutoRefreshCapability implements Capability, ApplicationContextAware { From 72e03d8553223be955a08f8aa52cd0f739fcdb05 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 26 Oct 2025 12:09:56 +0800 Subject: [PATCH 203/310] Refactor FeignComponentRegistry for improved clarity and validation Replaces manual map initialization with static factory methods, normalizes config keys, and adds input validation using assert methods. Refactors refresh logic for better separation of concerns and correctness, and improves code readability by extracting component presence checks into helper methods. --- .../autorefresh/FeignComponentRegistry.java | 124 ++++++++++-------- 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java index e2cd5112..ea21cbb7 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java @@ -10,25 +10,45 @@ import io.microsphere.spring.cloud.openfeign.components.CompositedRequestInterceptor; import io.microsphere.spring.cloud.openfeign.components.Refreshable; import org.springframework.beans.factory.BeanFactory; -import org.springframework.util.ObjectUtils; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.collection.Maps.ofMap; +import static io.microsphere.collection.Sets.ofSet; +import static io.microsphere.spring.boot.context.properties.source.util.ConfigurationPropertyUtils.toDashedForm; import static io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor.INSTANCE; +import static io.microsphere.util.Assert.assertNoNullElements; +import static io.microsphere.util.Assert.assertNotBlank; +import static io.microsphere.util.Assert.assertNotEmpty; +import static io.microsphere.util.Assert.assertNotNull; +import static io.microsphere.util.StringUtils.isBlank; /** + * Feign Component Registry + * * @author 韩超 + * @author Mercy * @since 0.0.1 */ public class FeignComponentRegistry { - private static final Map> configComponentMappings = new HashMap<>(16); + private static final Map> configComponentMappings = ofMap( + "retryer", Retryer.class, + "error-decoder", ErrorDecoder.class, + "request-interceptors", RequestInterceptor.class, + "default-request-headers", RequestInterceptor.class, + "default-query-parameters", RequestInterceptor.class, + "decoder", Decoder.class, + "encoder", Encoder.class, + "contract", Contract.class, + "query-map-encoder", QueryMapEncoder.class + ); private final Map> refreshableComponents = new ConcurrentHashMap<>(32); @@ -38,39 +58,20 @@ public class FeignComponentRegistry { private final BeanFactory beanFactory; - static { - configComponentMappings.put("retryer", Retryer.class); - configComponentMappings.put("errorDecoder", ErrorDecoder.class); - configComponentMappings.put("error-decoder", ErrorDecoder.class); - configComponentMappings.put("requestInterceptors", RequestInterceptor.class); - configComponentMappings.put("request-interceptors", RequestInterceptor.class); - configComponentMappings.put("defaultRequestHeaders", RequestInterceptor.class); - configComponentMappings.put("default-request-headers", RequestInterceptor.class); - configComponentMappings.put("defaultQueryParameters", RequestInterceptor.class); - configComponentMappings.put("default-query-parameters", RequestInterceptor.class); - configComponentMappings.put("decoder", Decoder.class); - configComponentMappings.put("encoder", Encoder.class); - configComponentMappings.put("contract", Contract.class); - configComponentMappings.put("queryMapEncoder", QueryMapEncoder.class); - configComponentMappings.put("query-map-encoder", QueryMapEncoder.class); - } - protected static Class getComponentClass(String config) { - if (ObjectUtils.isEmpty(config)) + if (isBlank(config)) { return null; - //组合 - if (config.endsWith("]")) { - for (Map.Entry> next : configComponentMappings.entrySet()) { - if (config.startsWith(next.getKey())) - return next.getValue(); - } - } else { + } + String normalizedConfig = toDashedForm(config); + // Composite + if (normalizedConfig.endsWith("]")) { for (Map.Entry> next : configComponentMappings.entrySet()) { - if (config.equals(next.getKey())) + if (normalizedConfig.startsWith(next.getKey())) { return next.getValue(); + } } } - return null; + return configComponentMappings.get(normalizedConfig); } public FeignComponentRegistry(String defaultClientName, BeanFactory beanFactory) { @@ -79,16 +80,20 @@ public FeignComponentRegistry(String defaultClientName, BeanFactory beanFactory) } public void register(String clientName, List components) { + assertNotBlank(clientName, () -> "The 'clientName' must not be blank!"); + assertNotEmpty(components, () -> "The 'components' must not be empty!"); + assertNoNullElements(components, () -> "The 'components' must not contain the null element!"); List componentList = this.refreshableComponents.computeIfAbsent(clientName, name -> new ArrayList<>()); - componentList.addAll(componentList); + componentList.addAll(components); } public void register(String clientName, Refreshable component) { - List componentList = this.refreshableComponents.computeIfAbsent(clientName, name -> new ArrayList<>()); - componentList.add(component); + register(clientName, ofList(component)); } public RequestInterceptor registerRequestInterceptor(String clientName, RequestInterceptor requestInterceptor) { + assertNotBlank(clientName, () -> "The 'clientName' must not be blank!"); + assertNotNull(requestInterceptor, () -> "The 'requestInterceptor' must not be null!"); CompositedRequestInterceptor compositedRequestInterceptor = this.interceptorsMap.computeIfAbsent(clientName, (name) -> new CompositedRequestInterceptor(clientName, beanFactory)); if (compositedRequestInterceptor.addRequestInterceptor(requestInterceptor)) { return compositedRequestInterceptor; @@ -97,16 +102,20 @@ public RequestInterceptor registerRequestInterceptor(String clientName, RequestI } - public synchronized void refresh(String clientName, Set changedConfig) { + public void refresh(String clientName, String... changedConfigs) { + refresh(clientName, ofSet(changedConfigs)); + } + + public synchronized void refresh(String clientName, Set changedConfigs) { + Set> effectiveComponents = new HashSet<>(changedConfigs.size()); - Set> effectiveComponents = new HashSet<>(); boolean hasInterceptor = false; - for (String value : changedConfig) { - value = value.replace(clientName + ".", ""); - Class clazz = getComponentClass(value); + for (String changedConfig : changedConfigs) { + changedConfig = changedConfig.replace(clientName + ".", ""); + Class clazz = getComponentClass(changedConfig); if (clazz != null) { effectiveComponents.add(clazz); - hasInterceptor = clazz.equals(RequestInterceptor.class); + hasInterceptor = RequestInterceptor.class.equals(clazz); } } @@ -114,37 +123,38 @@ public synchronized void refresh(String clientName, Set changedConfig) { //default configs changed, need refresh all refreshableComponents.values().stream() .flatMap(List::stream) - .filter(component -> { - Class componentsClass = component.getClass(); - for (Class actualComponent : effectiveComponents) - if (actualComponent.isAssignableFrom(componentsClass)) - return true; - return false; - }) + .filter(component -> isComponentPresent(component, effectiveComponents)) .forEach(Refreshable::refresh); - if (hasInterceptor) - this.interceptorsMap.values() - .forEach(CompositedRequestInterceptor::refresh); + if (hasInterceptor) { + this.interceptorsMap.values().forEach(CompositedRequestInterceptor::refresh); + } return; } + List components = this.refreshableComponents.get(clientName); - if (components != null) + if (components != null) { components.stream() - .filter(component -> { - Class componentsClass = component.getClass(); - for (Class actualComponent : effectiveComponents) - if (actualComponent.isAssignableFrom(componentsClass)) - return true; - return false; - }) + .filter(component -> isComponentPresent(component, effectiveComponents)) .forEach(Refreshable::refresh); + } if (hasInterceptor) { CompositedRequestInterceptor requestInterceptor = this.interceptorsMap.get(clientName); if (requestInterceptor != null) requestInterceptor.refresh(); } + } + static boolean isComponentPresent(Refreshable component, Iterable> effectiveComponents) { + return isComponentClassPresent(component.getClass(), effectiveComponents); } + static boolean isComponentClassPresent(Class componentsClass, Iterable> effectiveComponents) { + for (Class actualComponent : effectiveComponents) { + if (actualComponent.isAssignableFrom(componentsClass)) { + return true; + } + } + return false; + } } From a40db1e7dde96493399e50deb46dc4c428b29697 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 26 Oct 2025 12:10:00 +0800 Subject: [PATCH 204/310] Add unit tests for FeignComponentRegistry Introduces FeignComponentRegistryTest to verify component class resolution, registration, and refresh logic. Tests cover valid and invalid input scenarios for component registration and ensure correct behavior of request interceptor handling. --- .../FeignComponentRegistryTest.java | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java new file mode 100644 index 00000000..5f6b8d94 --- /dev/null +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.openfeign.autorefresh; + + +import feign.Contract; +import feign.QueryMapEncoder; +import feign.RequestInterceptor; +import feign.Retryer; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import io.microsphere.spring.cloud.openfeign.components.CompositedRequestInterceptor; +import io.microsphere.spring.cloud.openfeign.components.Refreshable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.List; + +import static io.microsphere.spring.cloud.openfeign.autorefresh.FeignComponentRegistry.getComponentClass; +import static io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor.INSTANCE; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link FeignComponentRegistry} Test + * + * @author Mercy + * @see FeignComponentRegistry + * @since 1.0.0 + */ +class FeignComponentRegistryTest { + + private String clientName = ""; + + private GenericApplicationContext context; + + private FeignComponentRegistry registry; + + @BeforeEach + void setUp() { + this.clientName = "test-client"; + this.context = new GenericApplicationContext(); + this.context.refresh(); + this.registry = new FeignComponentRegistry(this.clientName, this.context); + } + + @Test + void testGetComponentClass() { + + assertEquals(Retryer.class, getComponentClass("retryer")); + + assertEquals(ErrorDecoder.class, getComponentClass("error-decoder")); + assertEquals(ErrorDecoder.class, getComponentClass("errorDecoder")); + + assertEquals(RequestInterceptor.class, getComponentClass("request-interceptors")); + assertEquals(RequestInterceptor.class, getComponentClass("requestInterceptors")); + + assertEquals(RequestInterceptor.class, getComponentClass("default-request-headers")); + assertEquals(RequestInterceptor.class, getComponentClass("defaultRequestHeaders")); + + assertEquals(RequestInterceptor.class, getComponentClass("default-query-parameters")); + assertEquals(RequestInterceptor.class, getComponentClass("defaultQueryParameters")); + + assertEquals(Decoder.class, getComponentClass("decoder")); + + assertEquals(Encoder.class, getComponentClass("encoder")); + + assertEquals(Contract.class, getComponentClass("contract")); + + assertEquals(QueryMapEncoder.class, getComponentClass("query-map-encoder")); + assertEquals(QueryMapEncoder.class, getComponentClass("queryMapEncoder")); + + assertNull(getComponentClass("unknown")); + assertNull(getComponentClass("")); + assertNull(getComponentClass(" ")); + assertNull(getComponentClass(null)); + } + + @Test + void testGetComponentClassForMultipleConfigs() { + + assertEquals(Retryer.class, getComponentClass("retryer[0]")); + + assertEquals(ErrorDecoder.class, getComponentClass("error-decoder[0]")); + assertEquals(ErrorDecoder.class, getComponentClass("errorDecoder[0]")); + + assertEquals(RequestInterceptor.class, getComponentClass("request-interceptors[0]")); + assertEquals(RequestInterceptor.class, getComponentClass("requestInterceptors[0]")); + + assertEquals(RequestInterceptor.class, getComponentClass("default-request-headers[0]")); + assertEquals(RequestInterceptor.class, getComponentClass("defaultRequestHeaders[0]")); + + assertEquals(RequestInterceptor.class, getComponentClass("default-query-parameters[0]")); + assertEquals(RequestInterceptor.class, getComponentClass("defaultQueryParameters[0]")); + + assertEquals(Decoder.class, getComponentClass("decoder[0]")); + + assertEquals(Encoder.class, getComponentClass("encoder[0]")); + + assertEquals(Contract.class, getComponentClass("contract[0]")); + + assertEquals(QueryMapEncoder.class, getComponentClass("query-map-encoder[0]")); + assertEquals(QueryMapEncoder.class, getComponentClass("queryMapEncoder[0]")); + } + + @Test + void testRegisterOnIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> this.registry.register(null, (Refreshable) null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.register("", (Refreshable) null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.register(" ", (Refreshable) null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.register(this.clientName, (Refreshable) null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.register(this.clientName, (List) null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.register(this.clientName, emptyList())); + } + + @Test + void testRegisterRequestInterceptor() { + assertTrue(this.registry.registerRequestInterceptor(this.clientName, INSTANCE) instanceof CompositedRequestInterceptor); + assertSame(INSTANCE, this.registry.registerRequestInterceptor(this.clientName, INSTANCE)); + } + + @Test + void testRegisterRequestInterceptorOnIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> this.registry.registerRequestInterceptor(null, null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.registerRequestInterceptor("", null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.registerRequestInterceptor(" ", null)); + assertThrows(IllegalArgumentException.class, () -> this.registry.registerRequestInterceptor(this.clientName, null)); + } + + @Test + void testRefresh() { + testRefresh(this.clientName); + testRefresh("test-client-2"); + } + + void testRefresh(String clientName) { + this.registry.refresh(clientName, "retryer"); + this.registry.refresh(clientName, "error-decoder", "decoder", "encoder"); + this.registry.refresh(clientName, "request-interceptors", "default-request-headers", "default-query-parameters"); + } +} \ No newline at end of file From e0aca830f46dd83faff565c0bb9f0903406fade6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 26 Oct 2025 12:59:37 +0800 Subject: [PATCH 205/310] Expand test coverage for FeignComponentRegistry refresh Added additional calls to the registry.refresh method in FeignComponentRegistryTest to cover cases with unknown and indexed component names, improving test coverage for different input scenarios. --- .../openfeign/autorefresh/FeignComponentRegistryTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java index 5f6b8d94..4e24c119 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistryTest.java @@ -155,8 +155,13 @@ void testRefresh() { } void testRefresh(String clientName) { + this.registry.refresh(clientName, "unknown"); this.registry.refresh(clientName, "retryer"); this.registry.refresh(clientName, "error-decoder", "decoder", "encoder"); this.registry.refresh(clientName, "request-interceptors", "default-request-headers", "default-query-parameters"); + + this.registry.refresh(clientName, "retryer[0]"); + this.registry.refresh(clientName, "error-decoder[0]", "decoder[0]", "encoder[0]"); + this.registry.refresh(clientName, "request-interceptors[0]", "default-request-headers[0]", "default-query-parameters[0]"); } } \ No newline at end of file From 565bbc5d97226914246203e047fe85648620795b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 26 Oct 2025 17:21:46 +0800 Subject: [PATCH 206/310] Refactor config normalization in FeignComponentRegistry Extracted config normalization logic into a new static method normalizeConfig, simplifying getComponentClassByConfig and improving code clarity. --- .../autorefresh/FeignComponentRegistry.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java index ea21cbb7..45b1d84c 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java @@ -21,6 +21,7 @@ import static io.microsphere.collection.Lists.ofList; import static io.microsphere.collection.Maps.ofMap; import static io.microsphere.collection.Sets.ofSet; +import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET; import static io.microsphere.spring.boot.context.properties.source.util.ConfigurationPropertyUtils.toDashedForm; import static io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor.INSTANCE; import static io.microsphere.util.Assert.assertNoNullElements; @@ -28,6 +29,7 @@ import static io.microsphere.util.Assert.assertNotEmpty; import static io.microsphere.util.Assert.assertNotNull; import static io.microsphere.util.StringUtils.isBlank; +import static io.microsphere.util.StringUtils.substringBefore; /** * Feign Component Registry @@ -62,18 +64,15 @@ protected static Class getComponentClass(String config) { if (isBlank(config)) { return null; } - String normalizedConfig = toDashedForm(config); - // Composite - if (normalizedConfig.endsWith("]")) { - for (Map.Entry> next : configComponentMappings.entrySet()) { - if (normalizedConfig.startsWith(next.getKey())) { - return next.getValue(); - } - } - } + String normalizedConfig = normalizeConfig(config); return configComponentMappings.get(normalizedConfig); } + static String normalizeConfig(String config) { + String normalizedConfig = substringBefore(config, LEFT_SQUARE_BRACKET); + return toDashedForm(normalizedConfig); + } + public FeignComponentRegistry(String defaultClientName, BeanFactory beanFactory) { this.defaultClientName = defaultClientName; this.beanFactory = beanFactory; From 3292630deeeb2351ecb77f0256fa98e9d595f5c0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 26 Oct 2025 18:08:14 +0800 Subject: [PATCH 207/310] Update Maven profile from release to publish Changed the activated Maven profile in the publish workflow from 'release' to 'publish' to align with the intended deployment configuration. --- .github/workflows/maven-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 29da6fcd..6b421c08 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -44,7 +44,7 @@ jobs: -Drevision=${{ inputs.revision }} -Dgpg.skip=true deploy - --activate-profiles release,ci + --activate-profiles publish,ci env: MAVEN_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} From d47e5557de3bf07be9f6d7b491e94757ec088831 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 27 Oct 2025 15:25:46 +0800 Subject: [PATCH 208/310] Update project version to 0.2.2-SNAPSHOT Changed the property in pom.xml from 2.0.0-SNAPSHOT to 0.2.2-SNAPSHOT to reflect the new development version. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b6a2b380..a390fc7b 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - 2.0.0-SNAPSHOT + 0.2.2-SNAPSHOT 17 From b6742e46dc1eacef0be30b263438a6cdcc206b78 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 27 Oct 2025 15:53:42 +0800 Subject: [PATCH 209/310] Refactor ServiceInstanceUtils and update dependency version Removed the custom toJSON method and related test from ServiceInstanceUtils, now directly using WebEndpointMapping.toJSON. Also updated microsphere-spring-boot.version from 0.2.3 to 0.2.4 in the parent POM. --- .../service/util/ServiceInstanceUtils.java | 17 +---------------- .../service/util/ServiceInstanceUtilsTest.java | 13 ------------- microsphere-spring-cloud-parent/pom.xml | 2 +- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index e12b6016..779b1cbf 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -33,11 +33,8 @@ import static io.microsphere.collection.ListUtils.newArrayList; import static io.microsphere.constants.SeparatorConstants.LINE_SEPARATOR; -import static io.microsphere.constants.SymbolConstants.COLON; import static io.microsphere.constants.SymbolConstants.COMMA; -import static io.microsphere.constants.SymbolConstants.DOUBLE_QUOTE; import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET; -import static io.microsphere.constants.SymbolConstants.RIGHT_CURLY_BRACE; import static io.microsphere.constants.SymbolConstants.RIGHT_SQUARE_BRACKET; import static io.microsphere.json.JSONUtils.jsonArray; import static io.microsphere.json.JSONUtils.readArray; @@ -66,7 +63,7 @@ public class ServiceInstanceUtils extends BaseUtils { public static void attachMetadata(String contextPath, ServiceInstance serviceInstance, Collection webEndpointMappings) { Map metadata = serviceInstance.getMetadata(); StringJoiner jsonBuilder = new StringJoiner(COMMA + LINE_SEPARATOR, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET); - webEndpointMappings.stream().map(mapping -> toJSON(mapping)).forEach(jsonBuilder::add); + webEndpointMappings.stream().map(WebEndpointMapping::toJSON).forEach(jsonBuilder::add); String json = jsonBuilder.toString(); logger.trace("Web Endpoint Mappings JSON: \n{}", json); json = json.replace(LINE_SEPARATOR, EMPTY_STRING); @@ -91,18 +88,6 @@ public static Collection getWebEndpointMappings(ServiceInsta return parseWebEndpointMappings(encodedJSON); } - static String toJSON(WebEndpointMapping webEndpointMapping) { - // FIXME : Issue on WebEndpointMapping.toJSON() - String json = webEndpointMapping.toJSON(); - StringBuilder jsonBuilder = new StringBuilder(json); - int startIndex = jsonBuilder.lastIndexOf(LINE_SEPARATOR); - int endIndex = jsonBuilder.indexOf(RIGHT_CURLY_BRACE, startIndex); - String kindItem = COMMA + LINE_SEPARATOR + DOUBLE_QUOTE + "kind" + DOUBLE_QUOTE + COLON + - DOUBLE_QUOTE + webEndpointMapping.getKind() + DOUBLE_QUOTE + LINE_SEPARATOR; - jsonBuilder.replace(startIndex, endIndex, kindItem); - return jsonBuilder.toString(); - } - static List parseWebEndpointMappings(String encodedJSON) { if (isBlank(encodedJSON)) { return emptyList(); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 94a83314..99d3fc85 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -18,7 +18,6 @@ package io.microsphere.spring.cloud.client.service.util; -import io.microsphere.json.JSONObject; import io.microsphere.spring.web.metadata.WebEndpointMapping; import io.microsphere.spring.web.metadata.WebEndpointMapping.Builder; import org.junit.jupiter.api.BeforeEach; @@ -29,14 +28,11 @@ import java.util.Collection; import static io.microsphere.collection.Lists.ofList; -import static io.microsphere.json.JSONUtils.jsonObject; import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings; -import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMapping; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMappings; -import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.toJSON; import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.SERVLET; import static io.microsphere.spring.web.metadata.WebEndpointMapping.servlet; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; @@ -104,15 +100,6 @@ void testParseWebEndpointMappings() { assertSame(emptyList(), parseWebEndpointMappings(" ")); } - @Test - void testParseWebEndpointMapping() { - WebEndpointMapping webEndpointMapping = buildWebEndpointMapping(false); - String json = toJSON(webEndpointMapping); - JSONObject jsonObject = jsonObject(json); - WebEndpointMapping webEndpointMapping1 = parseWebEndpointMapping(jsonObject); - assertEquals(webEndpointMapping, webEndpointMapping1); - } - private Collection createWebEndpointMappings() { return ofList(buildWebEndpointMapping(true)); } diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 3ad34b18..97bad6bf 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -20,7 +20,7 @@ - 0.2.3 + 0.2.4 2.0.1 From 80f1eecb0b6eca06ddfac341b19b56fd72c9ddc2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 27 Oct 2025 15:59:20 +0800 Subject: [PATCH 210/310] Add test for parseWebEndpointMapping method Introduces a unit test to verify that parseWebEndpointMapping correctly reconstructs a WebEndpointMapping from a JSONObject. Ensures serialization and deserialization consistency. --- .../service/util/ServiceInstanceUtilsTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 99d3fc85..371c91d9 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -18,6 +18,7 @@ package io.microsphere.spring.cloud.client.service.util; +import io.microsphere.json.JSONObject; import io.microsphere.spring.web.metadata.WebEndpointMapping; import io.microsphere.spring.web.metadata.WebEndpointMapping.Builder; import org.junit.jupiter.api.BeforeEach; @@ -28,10 +29,12 @@ import java.util.Collection; import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.json.JSONUtils.jsonObject; import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMapping; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMappings; import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.SERVLET; import static io.microsphere.spring.web.metadata.WebEndpointMapping.servlet; @@ -93,6 +96,15 @@ void testGetWebEndpointMappings() { assertArrayEquals(EMPTY_STRING_ARRAY, webEndpointMapping.getConsumes()); } + @Test + void testParseWebEndpointMapping() { + WebEndpointMapping webEndpointMapping = buildWebEndpointMapping(false); + String json = webEndpointMapping.toJSON(); + JSONObject jsonObject = jsonObject(json); + WebEndpointMapping webEndpointMapping1 = parseWebEndpointMapping(jsonObject); + assertEquals(webEndpointMapping, webEndpointMapping1); + } + @Test void testParseWebEndpointMappings() { assertSame(emptyList(), parseWebEndpointMappings(null)); From 183015fd6f7ba5ca24b11e1059260ad923383835 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 27 Oct 2025 17:13:59 +0800 Subject: [PATCH 211/310] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9b6dc44..19fcb327 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ pom.xml: | **Branches** | **Purpose** | **Latest Version** | |--------------|--------------------------------------------------|--------------------| -| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.1 | -| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.1 | +| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.2 | +| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.2 | Then add the specific modules you need: From 7e2a8f023dae4a2123f06b9021c873068dab1c3f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 16:45:13 +0800 Subject: [PATCH 212/310] Bump project version to 0.2.3-SNAPSHOT Updated the property in pom.xml to prepare for the next development iteration. --- .../ReactiveDiscoveryClientAdapter.java | 68 ++++++++++++++ .../registry/SimpleServiceRegistry.java | 88 +++++++++++++++++++ .../ReactiveDiscoveryClientAdapterTest.java | 60 +++++++++++++ pom.xml | 2 +- 4 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java new file mode 100644 index 00000000..3b5ec6cd --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.discovery; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import reactor.core.publisher.Flux; + +import java.util.List; + +/** + * An adapter {@link DiscoveryClient} class based on {@link ReactiveDiscoveryClient} + * + * @author Mercy + * @see DiscoveryClient + * @since 1.0.0 + */ +public class ReactiveDiscoveryClientAdapter implements DiscoveryClient { + + private final ReactiveDiscoveryClient reactiveDiscoveryClient; + + public ReactiveDiscoveryClientAdapter(ReactiveDiscoveryClient reactiveDiscoveryClient) { + this.reactiveDiscoveryClient = reactiveDiscoveryClient; + } + + @Override + public String description() { + return this.reactiveDiscoveryClient.description(); + } + + @Override + public List getInstances(String serviceId) { + Flux flux = this.reactiveDiscoveryClient.getInstances(serviceId); + return flux.collectList().block(); + } + + @Override + public List getServices() { + Flux flux = this.reactiveDiscoveryClient.getServices(); + return flux.collectList().block(); + } + + @Override + public void probe() { + this.reactiveDiscoveryClient.probe(); + } + + @Override + public int getOrder() { + return this.reactiveDiscoveryClient.getOrder(); + } +} diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java new file mode 100644 index 00000000..b226cd89 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry; + +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Simple {@link ServiceRegistry} class that is based on {@link SimpleDiscoveryProperties} to register + * {@link DefaultRegistration}. + * + * @author Mercy + * @see ServiceRegistry + * @see DefaultRegistration + * @see SimpleDiscoveryProperties#getInstances() + * @since 1.0.0 + */ +public class SimpleServiceRegistry implements ServiceRegistry { + + private static final String STATUS_KEY = "_status_"; + + private final Map> instancesMap; + + public SimpleServiceRegistry(SimpleDiscoveryProperties simpleDiscoveryProperties) { + this.instancesMap = simpleDiscoveryProperties.getInstances(); + } + + @Override + public void register(DefaultRegistration registration) { + List instances = getInstances(registration); + instances.add(registration); + } + + @Override + public void deregister(DefaultRegistration registration) { + List instances = getInstances(registration); + instances.remove(registration); + } + + @Override + public void close() { + } + + @Override + public void setStatus(DefaultRegistration registration, String status) { + Map metadata = getMetadata(registration); + metadata.put(STATUS_KEY, status); + } + + @Override + public T getStatus(DefaultRegistration registration) { + Map metadata = getMetadata(registration); + return (T) metadata.get(STATUS_KEY); + } + + List getInstances(DefaultRegistration registration) { + return getInstances(registration.getServiceId()); + } + + List getInstances(String serviceId) { + return this.instancesMap.computeIfAbsent(serviceId, k -> new ArrayList<>()); + } + + Map getMetadata(Registration registration) { + return registration.getMetadata(); + } +} diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java new file mode 100644 index 00000000..2e5e2b29 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.discovery; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; + +/** + * {@link ReactiveDiscoveryClientAdapter} + * + * @author Mercy + * @see ReactiveDiscoveryClientAdapter + * @see ReactiveDiscoveryClient + * @see DiscoveryClient + * @since 1.0.0 + */ +class ReactiveDiscoveryClientAdapterTest { + + @BeforeEach + void setUp() { + } + + @Test + void testDescription() { + } + + @Test + void testGetInstances() { + } + + @Test + void testGetServices() { + } + + @Test + void testProbe() { + } + + @Test + void testGetOrder() { + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index a390fc7b..a43647f1 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - 0.2.2-SNAPSHOT + 0.2.3-SNAPSHOT 17 From 2846fbd4ddef757e2731992d6368d65cf540f2c9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:44:59 +0800 Subject: [PATCH 213/310] Add constant for ReactiveCommonsClientAutoConfiguration Introduced REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME to DiscoveryClientConstants for referencing the ReactiveCommonsClientAutoConfiguration class. This improves consistency and supports scenarios requiring the reactive auto-configuration class name. --- .../constants/DiscoveryClientConstants.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java index f5b9f10c..505c5a9d 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java @@ -17,6 +17,7 @@ package io.microsphere.spring.cloud.client.discovery.constants; import org.springframework.cloud.client.CommonsClientAutoConfiguration; +import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; @@ -30,17 +31,29 @@ public interface DiscoveryClientConstants { /** * The class name of {@link DiscoveryClient} + * + * @see org.springframework.cloud.client.discovery.DiscoveryClient */ String DISCOVERY_CLIENT_CLASS_NAME = "org.springframework.cloud.client.discovery.DiscoveryClient"; /** * The class name of {@link CompositeDiscoveryClient} + * + * @see org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient */ String COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME = "org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient"; /** * The class name of {@link CommonsClientAutoConfiguration} + * + * @see org.springframework.cloud.client.CommonsClientAutoConfiguration */ String COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.client.CommonsClientAutoConfiguration"; -} + /** + * The class name of {@link ReactiveCommonsClientAutoConfiguration} + * + * @see org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration + */ + String REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration"; +} \ No newline at end of file From 968f3049c0c89e2c6c3af5a0cba9aac685a68f42 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:45:11 +0800 Subject: [PATCH 214/310] Add test for REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME Added an assertion in DiscoveryClientConstantsTest to verify the value of REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME, ensuring coverage for this constant. --- .../discovery/constants/DiscoveryClientConstantsTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java index cadb2c57..b92ae186 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java @@ -21,6 +21,7 @@ import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -37,5 +38,6 @@ void testConstants() { assertEquals("org.springframework.cloud.client.discovery.DiscoveryClient", DISCOVERY_CLIENT_CLASS_NAME); assertEquals("org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient", COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME); assertEquals("org.springframework.cloud.client.CommonsClientAutoConfiguration", COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); + assertEquals("org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration", REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); } -} +} \ No newline at end of file From 87f874d3ac525395560268db3f209bb68693a059 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:45:18 +0800 Subject: [PATCH 215/310] Remove trailing newline at end of Tomcat config file Deleted the trailing newline at the end of TomcatFaultToleranceAutoConfiguration.java to maintain consistent file formatting. --- .../autoconfigure/TomcatFaultToleranceAutoConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java index ea12ae9a..58b1cd92 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java @@ -88,4 +88,4 @@ public void onWebServerInitializedEvent(WebServerInitializedEvent event) { }); } } -} +} \ No newline at end of file From defd11a63694b4e20409335517d8cd72d7b7f623 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:46:32 +0800 Subject: [PATCH 216/310] Remove trailing newlines at end of Java source files This commit removes the trailing newline at the end of multiple Java source files in the microsphere-spring-cloud-commons module to ensure consistency in file formatting. --- .../cloud/client/event/ServiceInstancesChangedEvent.java | 2 +- .../cloud/client/service/registry/DefaultRegistration.java | 2 +- .../cloud/client/service/registry/InMemoryServiceRegistry.java | 2 +- .../service/registry/MultipleAutoServiceRegistration.java | 2 +- .../cloud/client/service/registry/MultipleRegistration.java | 2 +- .../cloud/client/service/registry/MultipleServiceRegistry.java | 2 +- .../cloud/client/service/registry/RegistrationCustomizer.java | 2 +- .../cloud/client/service/registry/RegistrationMetaData.java | 2 +- .../client/service/registry/SimpleAutoServiceRegistration.java | 2 +- .../cloud/client/service/registry/SimpleServiceRegistry.java | 2 +- .../ServiceRegistrationEndpointAutoConfiguration.java | 2 +- .../registry/aspect/EventPublishingRegistrationAspect.java | 2 +- .../autoconfigure/ServiceRegistryAutoConfiguration.java | 2 +- .../autoconfigure/WebFluxServiceRegistryAutoConfiguration.java | 2 +- .../client/service/registry/constants/InstanceConstants.java | 2 +- .../registry/endpoint/AbstractServiceRegistrationEndpoint.java | 2 +- .../registry/endpoint/ServiceDeregistrationEndpoint.java | 2 +- .../service/registry/event/RegistrationDeregisteredEvent.java | 2 +- .../cloud/client/service/registry/event/RegistrationEvent.java | 2 +- .../service/registry/event/RegistrationPreRegisteredEvent.java | 2 +- .../spring/cloud/client/service/util/ServiceInstanceUtils.java | 2 +- .../cloud/commons/constants/SpringCloudPropertyConstants.java | 2 +- .../tolerance/constants/FaultTolerancePropertyConstants.java | 3 +-- .../cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java | 2 +- .../fault/tolerance/loadbalancer/util/LoadBalancerUtils.java | 2 +- 25 files changed, 25 insertions(+), 26 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java index 805706ae..2515d02b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/event/ServiceInstancesChangedEvent.java @@ -86,4 +86,4 @@ public boolean isProcessed() { return processed; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistration.java index d15acf15..bd3eb25b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/DefaultRegistration.java @@ -27,4 +27,4 @@ * @since 1.0.0 */ public class DefaultRegistration extends DefaultServiceInstance implements Registration { -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java index d3191d8e..26da337e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java @@ -77,4 +77,4 @@ protected Map getMetadata(Registration registration) { } return null; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java index 901d3c2b..70b06231 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java @@ -47,4 +47,4 @@ protected MultipleRegistration getRegistration() { protected MultipleRegistration getManagementRegistration() { return this.multipleRegistration; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java index dd3b9d55..23783914 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java @@ -85,4 +85,4 @@ public T special(Class specialClass) { return (T) this; return (T) this.registrationMap.getOrDefault(specialClass, null); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java index 4ab21be5..4b5aa292 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java @@ -113,4 +113,4 @@ static Class getRegistrationClass(Class serviceRegist } return (Class) registrationClass; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationCustomizer.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationCustomizer.java index eccef247..8884b42b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationCustomizer.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationCustomizer.java @@ -33,4 +33,4 @@ public interface RegistrationCustomizer { * @param registration {@link Registration} */ void customize(Registration registration); -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java index e32da6b4..3701417e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java @@ -142,4 +142,4 @@ private void initializeIfZookeeperRegistrationAvailable(Registration registratio invokeMethod(registration, GET_SERVICE_INSTANCE_METHOD_NAME); } } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java index 1acc725f..c0409a1b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java @@ -60,4 +60,4 @@ protected Registration getRegistration() { protected Registration getManagementRegistration() { return registration; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java index b226cd89..27fdcffa 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java @@ -85,4 +85,4 @@ List getInstances(String serviceId) { Map getMetadata(Registration registration) { return registration.getMetadata(); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java index c2a73cbb..3b336488 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java @@ -62,4 +62,4 @@ public ServiceRegistrationEndpoint serviceRegistrationEndpoint() { public ServiceDeregistrationEndpoint serviceDeregistrationEndpoint() { return new ServiceDeregistrationEndpoint(); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java index d6112a08..de785f4b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java @@ -103,4 +103,4 @@ public void setApplicationContext(ApplicationContext applicationContext) throws this.context = applicationContext; this.registrationCustomizers = applicationContext.getBeanProvider(RegistrationCustomizer.class); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java index 2fa3b76e..ba838d6d 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java @@ -80,4 +80,4 @@ public MultipleAutoServiceRegistration multipleAutoServiceRegistration(MultipleR } } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java index cafa1eb5..4dbdff1e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java @@ -40,4 +40,4 @@ protected String getContextPath() { protected boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns) { return false; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java index 7128f753..a6c651e2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/constants/InstanceConstants.java @@ -43,4 +43,4 @@ public interface InstanceConstants { * The metadata name of start time */ String START_TIME_METADATA_NAME = "start-time"; -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java index 7e2dae49..5c25b8bb 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java @@ -71,4 +71,4 @@ protected boolean isRunning() { public void setRunning(boolean running) { this.running = running; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java index 732e5ea2..a3a5e418 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java @@ -27,4 +27,4 @@ public boolean stop() { } return isRunning; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java index 898b0397..de176582 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java @@ -39,4 +39,4 @@ public Type getType() { return DEREGISTERED; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java index b8e1b26f..84d9aa3a 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java @@ -150,4 +150,4 @@ public static enum Type { */ DEREGISTERED } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java index ccceec17..64073258 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java @@ -38,4 +38,4 @@ public RegistrationPreRegisteredEvent(ServiceRegistry registry, Re public Type getType() { return PRE_REGISTERED; } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 779b1cbf..97803f1c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -148,4 +148,4 @@ public static String getMetadata(ServiceInstance serviceInstance, String metadat private ServiceInstanceUtils() { } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java index 17b793f0..bcabfd50 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java @@ -68,4 +68,4 @@ public interface SpringCloudPropertyConstants { source = APPLICATION_SOURCE ) String FEATURES_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "features." + ENABLED_PROPERTY_NAME; -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java index 216cc000..18b65053 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/constants/FaultTolerancePropertyConstants.java @@ -54,5 +54,4 @@ public interface FaultTolerancePropertyConstants { * The default property value of weight : 100 */ int DEFAULT_WEIGHT_PROPERTY_VALUE = 100; - -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java index 1e65a771..dc2f30d8 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java @@ -62,4 +62,4 @@ public String toString() { .add("lastUpdate=" + lastUpdate) .toString(); } -} +} \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java index d7a38353..83424784 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/util/LoadBalancerUtils.java @@ -24,4 +24,4 @@ public static int calculateWarmupWeight(long uptime, long warmup, int weight) { private LoadBalancerUtils() { } -} +} \ No newline at end of file From 6ebb04d5c9a6eb1551e1d6c288e54fe541e7b068 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:46:43 +0800 Subject: [PATCH 217/310] Remove trailing newline at end of file Deleted the trailing newline at the end of DiscoveryClientAutoConfiguration.java to maintain consistency with file formatting standards. --- .../autoconfigure/DiscoveryClientAutoConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java index f1224270..28f9b1e7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java @@ -85,4 +85,4 @@ public UnionDiscoveryClient unionDiscoveryClient() { return new UnionDiscoveryClient(); } } -} +} \ No newline at end of file From 07150fd2df8cba482bdc5910136abfd98784728a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:46:54 +0800 Subject: [PATCH 218/310] Make ServiceInstanceUtilsTest class public Changed the visibility of ServiceInstanceUtilsTest from package-private to public to allow access from other packages, likely for improved test discoverability or integration. --- .../cloud/client/service/util/ServiceInstanceUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 371c91d9..a9c93bfc 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -53,7 +53,7 @@ * @see ServiceInstanceUtils * @since 1.0.0 */ -class ServiceInstanceUtilsTest { +public class ServiceInstanceUtilsTest { private static final Integer WEB_ENDPOINT_MAPPING_ID = Integer.valueOf(12345); From 084c75c792933ca836c012342b54ed40417e5207 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:47:04 +0800 Subject: [PATCH 219/310] Add SimpleDiscoveryClientAutoConfiguration dependency Updated SimpleAutoServiceRegistrationAutoConfiguration to auto-configure after SimpleDiscoveryClientAutoConfiguration. This ensures proper initialization order for service registration components. --- .../SimpleAutoServiceRegistrationAutoConfiguration.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java index 0182b574..7453eef1 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java @@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration; import org.springframework.cloud.client.serviceregistry.Registration; @@ -57,7 +58,8 @@ }) @AutoConfigureAfter(value = { UtilAutoConfiguration.class, - AutoServiceRegistrationConfiguration.class + AutoServiceRegistrationConfiguration.class, + SimpleDiscoveryClientAutoConfiguration.class }) @Import(value = { SimpleAutoServiceRegistration.class @@ -101,4 +103,4 @@ public Registration registration( public ServiceRegistry serviceRegistry() { return new InMemoryServiceRegistry(); } -} +} \ No newline at end of file From 0ed9935d92df6fc1f4e97d92b65efc26c0182ac1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:47:12 +0800 Subject: [PATCH 220/310] Refactor to extract Flux-to-List logic into helper method Moved repeated Flux-to-List conversion logic into a static toList() helper method in ReactiveDiscoveryClientAdapter to improve code reuse and readability. --- .../discovery/ReactiveDiscoveryClientAdapter.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java index 3b5ec6cd..12f55e19 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java @@ -47,13 +47,13 @@ public String description() { @Override public List getInstances(String serviceId) { Flux flux = this.reactiveDiscoveryClient.getInstances(serviceId); - return flux.collectList().block(); + return toList(flux); } @Override public List getServices() { Flux flux = this.reactiveDiscoveryClient.getServices(); - return flux.collectList().block(); + return toList(flux); } @Override @@ -65,4 +65,8 @@ public void probe() { public int getOrder() { return this.reactiveDiscoveryClient.getOrder(); } -} + + static List toList(Flux flux) { + return flux.collectList().block(); + } +} \ No newline at end of file From 8410de22f1b656ff4d2e92dd1e7cab44921574d6 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:47:22 +0800 Subject: [PATCH 221/310] Refactor ReactiveDiscoveryClientAdapterTest setup and assertions Refactors the test class to use a real SimpleReactiveDiscoveryClient with test data, adds assertions to all test methods, and improves test coverage by verifying expected values. This enhances the reliability and clarity of the unit tests for ReactiveDiscoveryClientAdapter. --- .../ReactiveDiscoveryClientAdapterTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java index 2e5e2b29..c6ae1384 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java @@ -20,8 +20,21 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClient; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryProperties; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtilsTest.createDefaultServiceInstance; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; /** * {@link ReactiveDiscoveryClientAdapter} @@ -34,27 +47,54 @@ */ class ReactiveDiscoveryClientAdapterTest { + private DefaultServiceInstance serviceInstance; + + private String appName = "test-service"; + + private SimpleReactiveDiscoveryProperties properties; + + private ReactiveDiscoveryClient client; + + private ReactiveDiscoveryClientAdapter adapter; + @BeforeEach void setUp() { + Map> instances = new HashMap<>(); + this.serviceInstance = createDefaultServiceInstance(); + this.appName = this.serviceInstance.getServiceId(); + instances.put(appName, ofList(this.serviceInstance)); + this.properties = new SimpleReactiveDiscoveryProperties(); + this.properties.setInstances(instances); + this.client = new SimpleReactiveDiscoveryClient(properties); + this.adapter = new ReactiveDiscoveryClientAdapter(client); } @Test void testDescription() { + assertEquals("Simple Reactive Discovery Client", this.adapter.description()); } @Test void testGetInstances() { + List serviceInstances = this.adapter.getInstances(this.appName); + assertEquals(1, serviceInstances.size()); + assertSame(this.serviceInstance, serviceInstances.get(0)); } @Test void testGetServices() { + List services = this.adapter.getServices(); + assertEquals(1, services.size()); + assertSame(appName, services.get(0)); } @Test void testProbe() { + this.adapter.probe(); } @Test void testGetOrder() { + assertEquals(this.client.getOrder(), this.adapter.getOrder()); } } \ No newline at end of file From d6aa228589b8f4d6363c0cfe447a6a65cf040228 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:47:29 +0800 Subject: [PATCH 222/310] Add ReactiveDiscoveryClient auto-configuration class Introduces ReactiveDiscoveryClientAutoConfiguration to provide auto-configuration for ReactiveDiscoveryClient, including a bean for ReactiveDiscoveryClientAdapter when a ReactiveDiscoveryClient is present. This enables integration with Spring Cloud's reactive discovery infrastructure. --- ...ctiveDiscoveryClientAutoConfiguration.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java new file mode 100644 index 00000000..ec164f29 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.discovery.autoconfigure; + +import io.microsphere.spring.cloud.client.discovery.ReactiveDiscoveryClientAdapter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled; +import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled; +import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; + +/** + * The Auto-Configuration class for {@link ReactiveDiscoveryClient} + * + * @author Mercy + * @see ReactiveDiscoveryClient + * @see DiscoveryClientAutoConfiguration + * @since 1.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(name = { + DISCOVERY_CLIENT_CLASS_NAME +}) +@ConditionalOnDiscoveryEnabled +@ConditionalOnReactiveDiscoveryEnabled +@AutoConfigureBefore(name = { + REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME +}) +public class ReactiveDiscoveryClientAutoConfiguration { + + @Configuration(proxyBeanMethods = false) + @ConditionalOnBlockingDiscoveryEnabled + public static class BlockingConfiguration { + + @Bean + @ConditionalOnBean(ReactiveDiscoveryClient.class) + public ReactiveDiscoveryClientAdapter reactiveDiscoveryClientAdapter(ReactiveDiscoveryClient reactiveDiscoveryClient) { + return new ReactiveDiscoveryClientAdapter(reactiveDiscoveryClient); + } + } +} \ No newline at end of file From 91e5ddb2639f0bfb1cd8be3cf98c66e06a916920 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:47:34 +0800 Subject: [PATCH 223/310] Add test for ReactiveDiscoveryClientAutoConfiguration Introduces a JUnit test to verify the configuration and integration of ReactiveDiscoveryClientAutoConfiguration with Spring Cloud's discovery client setup. The test ensures that the CompositeDiscoveryClient is correctly composed with the ReactiveDiscoveryClientAdapter and validates service discovery behavior. --- ...eDiscoveryClientAutoConfigurationTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfigurationTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfigurationTest.java new file mode 100644 index 00000000..529b0941 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfigurationTest.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 io.microsphere.spring.cloud.client.discovery.autoconfigure; + + +import io.microsphere.spring.cloud.client.discovery.ReactiveDiscoveryClientAdapter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; +import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration; +import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static io.microsphere.collection.Lists.ofList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * {@link ReactiveDiscoveryClientAutoConfiguration} Test + * + * @author Mercy + * @see ReactiveDiscoveryClientAutoConfiguration + * @since 1.0.0 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = { + UtilAutoConfiguration.class, + SimpleReactiveDiscoveryClientAutoConfiguration.class, + CompositeDiscoveryClientAutoConfiguration.class, + ReactiveDiscoveryClientAutoConfiguration.class +}) +@TestPropertySource( + properties = { + "spring.cloud.discovery.client.simple.instances.test[0].instanceId=1", + "spring.cloud.discovery.client.simple.instances.test[0].serviceId=test", + "spring.cloud.discovery.client.simple.instances.test[0].host=127.0.0.1", + "spring.cloud.discovery.client.simple.instances.test[0].port=8080", + "spring.cloud.discovery.client.simple.instances.test[0].metadata.key-1=value-1" + } +) +class ReactiveDiscoveryClientAutoConfigurationTest { + + @Autowired + private DiscoveryClient discoveryClient; + + @Autowired + private ReactiveDiscoveryClientAdapter adapter; + + @Test + void test() { + assertEquals(CompositeDiscoveryClient.class, this.discoveryClient.getClass()); + CompositeDiscoveryClient compositeDiscoveryClient = CompositeDiscoveryClient.class.cast(this.discoveryClient); + List discoveryClients = compositeDiscoveryClient.getDiscoveryClients(); + assertEquals(1, discoveryClients.size()); + assertSame(this.adapter, discoveryClients.get(0)); + List services = compositeDiscoveryClient.getServices(); + assertEquals(ofList("test"), services); + assertEquals(services, discoveryClients.get(0).getServices()); + assertEquals(services, this.adapter.getServices()); + } +} \ No newline at end of file From 7c4d32928477d2ba5d05ebe113dc24aa516da336 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 17:47:39 +0800 Subject: [PATCH 224/310] Add ReactiveDiscoveryClientAutoConfiguration import Included ReactiveDiscoveryClientAutoConfiguration in the AutoConfiguration.imports file to enable auto-configuration support for reactive discovery clients. --- ....springframework.boot.autoconfigure.AutoConfiguration.imports | 1 + 1 file changed, 1 insertion(+) diff --git a/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 55c28f69..fd5ff8c4 100644 --- a/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/microsphere-spring-cloud-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,4 +1,5 @@ io.microsphere.spring.cloud.client.discovery.autoconfigure.DiscoveryClientAutoConfiguration +io.microsphere.spring.cloud.client.discovery.autoconfigure.ReactiveDiscoveryClientAutoConfiguration io.microsphere.spring.cloud.client.service.registry.autoconfigure.ServiceRegistryAutoConfiguration io.microsphere.spring.cloud.client.service.registry.autoconfigure.WebMvcServiceRegistryAutoConfiguration io.microsphere.spring.cloud.client.service.registry.autoconfigure.WebFluxServiceRegistryAutoConfiguration From f6ac59b75b7558f139c100207ada993ac508d9d9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:09:51 +0800 Subject: [PATCH 225/310] Update expected discovery clients count in test #78 Changed the expected number of discovery clients in UnionDiscoveryClientIntegrationTest from 6 to 7 to reflect recent changes in the discovery client composition. --- .../UnionDiscoveryClientIntegrationTest.java | 2 +- .../registry/SimpleServiceRegistryTest.java | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientIntegrationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientIntegrationTest.java index bba97aea..be79b427 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientIntegrationTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClientIntegrationTest.java @@ -86,7 +86,7 @@ void test() { assertEquals(CompositeDiscoveryClient.class, discoveryClient.getClass()); CompositeDiscoveryClient compositeDiscoveryClient = CompositeDiscoveryClient.class.cast(discoveryClient); List discoveryClients = compositeDiscoveryClient.getDiscoveryClients(); - assertEquals(6, discoveryClients.size()); + assertEquals(7, discoveryClients.size()); assertEquals(UnionDiscoveryClient.class, discoveryClients.get(0).getClass()); List services = compositeDiscoveryClient.getServices(); assertTrue(services.size() > 1); diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java new file mode 100644 index 00000000..f1f66aa1 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.service.registry; + + +import org.junit.jupiter.api.Test; + +/** + * {@link SimpleServiceRegistry} Test + * + * @author Mercy + * @see SimpleServiceRegistry + * @since 1.0.0 + */ +class SimpleServiceRegistryTest { + + @Test + void testRegister() { + } + + @Test + void testDeregister() { + } + + @Test + void testClose() { + } + + @Test + void testSetStatus() { + } + + @Test + void testGetStatus() { + } +} \ No newline at end of file From 816ddf231aa315d662a3158cb1fa998fffc54f87 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:40:21 +0800 Subject: [PATCH 226/310] Make STATUS_KEY public in SimpleServiceRegistry Changed the STATUS_KEY field from private to public in SimpleServiceRegistry to allow access from outside the class. --- .../cloud/client/service/registry/SimpleServiceRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java index 27fdcffa..039298a2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java @@ -38,7 +38,7 @@ */ public class SimpleServiceRegistry implements ServiceRegistry { - private static final String STATUS_KEY = "_status_"; + public static final String STATUS_KEY = "_status_"; private final Map> instancesMap; From 2b4ca2505ba779cc1ffd9b4154a7bce06ba7e6e8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:40:26 +0800 Subject: [PATCH 227/310] Refactor and enhance SimpleServiceRegistryTest Refactored the test class to use @BeforeEach for setup, added utility methods for instance retrieval, and improved test coverage for register, deregister, close, setStatus, and getStatus methods. This improves test clarity, maintainability, and ensures more thorough validation of SimpleServiceRegistry behavior. --- .../registry/SimpleServiceRegistryTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java index f1f66aa1..028a8781 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java @@ -18,7 +18,20 @@ package io.microsphere.spring.cloud.client.service.registry; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; + +import java.util.List; +import java.util.Map; + +import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; +import static io.microsphere.spring.cloud.client.service.registry.SimpleServiceRegistry.STATUS_KEY; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link SimpleServiceRegistry} Test @@ -29,23 +42,72 @@ */ class SimpleServiceRegistryTest { + private DefaultRegistration registration; + + private SimpleDiscoveryProperties properties; + + private SimpleServiceRegistry registry; + + @BeforeEach + void setUp() { + this.registration = createDefaultRegistration(); + this.properties = new SimpleDiscoveryProperties(); + this.registry = new SimpleServiceRegistry(this.properties); + } + @Test void testRegister() { + Map> instancesMap = this.properties.getInstances(); + assertTrue(instancesMap.isEmpty()); + this.registry.register(this.registration); + + List instances = instancesMap.get(this.registration.getServiceId()); + assertEquals(1, instances.size()); + DefaultServiceInstance instance = instances.get(0); + assertSame(instance, this.registration); } @Test void testDeregister() { + testRegister(); + this.registry.deregister(this.registration); + List instances = getInstances(this.registration.getServiceId()); + assertTrue(instances.isEmpty()); } @Test void testClose() { + Map> instancesMap = this.properties.getInstances(); + assertTrue(instancesMap.isEmpty()); + this.registry.close(); + assertTrue(instancesMap.isEmpty()); } @Test void testSetStatus() { + testRegister(); + DefaultServiceInstance instance = getInstance(this.registration.getServiceId(), this.registration.getInstanceId()); + String status = "UP"; + this.registry.setStatus(this.registration, status); + assertEquals(status, instance.getMetadata().get(STATUS_KEY)); } @Test void testGetStatus() { + testRegister(); + String status = "UP"; + this.registry.setStatus(this.registration, status); + assertEquals(status, this.registry.getStatus(this.registration)); + } + + List getInstances(String serviceId) { + return this.properties.getInstances().getOrDefault(serviceId, emptyList()); + } + + DefaultServiceInstance getInstance(String serviceId, String instanceId) { + List instances = getInstances(serviceId); + return instances.stream() + .filter(instance -> instance.getInstanceId().equals(instanceId)) + .findFirst().orElse(null); } } \ No newline at end of file From 1f9ea52196d6802906f007e87aab09b7a33040f2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:55:28 +0800 Subject: [PATCH 228/310] Add setMetadata utility method to ServiceInstanceUtils Introduces a static setMetadata method to allow setting metadata values on a ServiceInstance. Also refactors getWebEndpointMappings to use getMetadata for consistency. --- .../service/util/ServiceInstanceUtils.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 97803f1c..1a61218f 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -83,8 +83,7 @@ public static void attachMetadata(String contextPath, ServiceInstance serviceIns */ @Nonnull public static Collection getWebEndpointMappings(ServiceInstance serviceInstance) { - Map metadata = serviceInstance.getMetadata(); - String encodedJSON = metadata.get(WEB_MAPPINGS_METADATA_NAME); + String encodedJSON = getMetadata(serviceInstance, WEB_MAPPINGS_METADATA_NAME); return parseWebEndpointMappings(encodedJSON); } @@ -146,6 +145,19 @@ public static String getMetadata(ServiceInstance serviceInstance, String metadat return metadata.get(metadataName); } + /** + * Set metadata by metadataName + * + * @param serviceInstance {@link ServiceInstance} + * @param metadataName metadataName + * @param metadataValue metadata value + * @return the previous value associated with metadataName if found, null otherwise + */ + public static String setMetadata(ServiceInstance serviceInstance, String metadataName, String metadataValue) { + Map metadata = serviceInstance.getMetadata(); + return metadata.put(metadataName, metadataValue); + } + private ServiceInstanceUtils() { } } \ No newline at end of file From 995d0904710047c2513d984d53233179b95292a8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:55:33 +0800 Subject: [PATCH 229/310] Add test for getMetadata and setMetadata methods Introduces a new test case to verify the behavior of getMetadata and setMetadata in ServiceInstanceUtils, ensuring metadata can be set and retrieved as expected. --- .../client/service/util/ServiceInstanceUtilsTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index a9c93bfc..6ba49adc 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -36,6 +36,7 @@ import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMapping; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMappings; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setMetadata; import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.SERVLET; import static io.microsphere.spring.web.metadata.WebEndpointMapping.servlet; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; @@ -43,6 +44,7 @@ import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -112,6 +114,13 @@ void testParseWebEndpointMappings() { assertSame(emptyList(), parseWebEndpointMappings(" ")); } + @Test + void testGetAndSetMetadata() { + assertNull(getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + setMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, this.context); + assertEquals(this.context, getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + } + private Collection createWebEndpointMappings() { return ofList(buildWebEndpointMapping(true)); } From 916dbd1703a2e98b5b94fcf1f07c60b2a0871526 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:55:44 +0800 Subject: [PATCH 230/310] Refactor metadata handling in SimpleServiceRegistry Replaces direct metadata manipulation with utility methods from ServiceInstanceUtils for setting and getting metadata. Also changes getStatus to return String instead of a generic type, and removes the internal getMetadata method. --- .../service/registry/SimpleServiceRegistry.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java index 039298a2..262a4e05 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java @@ -19,13 +19,15 @@ import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; -import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import java.util.ArrayList; import java.util.List; import java.util.Map; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getMetadata; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setMetadata; + /** * Simple {@link ServiceRegistry} class that is based on {@link SimpleDiscoveryProperties} to register * {@link DefaultRegistration}. @@ -64,14 +66,12 @@ public void close() { @Override public void setStatus(DefaultRegistration registration, String status) { - Map metadata = getMetadata(registration); - metadata.put(STATUS_KEY, status); + setMetadata(registration, STATUS_KEY, status); } @Override - public T getStatus(DefaultRegistration registration) { - Map metadata = getMetadata(registration); - return (T) metadata.get(STATUS_KEY); + public String getStatus(DefaultRegistration registration) { + return getMetadata(registration, STATUS_KEY); } List getInstances(DefaultRegistration registration) { @@ -81,8 +81,4 @@ List getInstances(DefaultRegistration registration) { List getInstances(String serviceId) { return this.instancesMap.computeIfAbsent(serviceId, k -> new ArrayList<>()); } - - Map getMetadata(Registration registration) { - return registration.getMetadata(); - } } \ No newline at end of file From 8778b0174f4e8eda7b9a9f2034024d1f62323b41 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:56:48 +0800 Subject: [PATCH 231/310] Improve ServiceInstanceUtilsTest coverage and clarity Refactored tests to use EMPTY_STRING constant for clarity and added assertions to test setMetadata behavior when updating values. These changes enhance test readability and ensure correct metadata handling. --- .../client/service/util/ServiceInstanceUtilsTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 6ba49adc..6d2477c0 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -39,6 +39,7 @@ import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setMetadata; import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.SERVLET; import static io.microsphere.spring.web.metadata.WebEndpointMapping.servlet; +import static io.microsphere.util.StringUtils.EMPTY_STRING; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; import static java.lang.System.currentTimeMillis; import static java.util.Collections.emptyList; @@ -110,15 +111,17 @@ void testParseWebEndpointMapping() { @Test void testParseWebEndpointMappings() { assertSame(emptyList(), parseWebEndpointMappings(null)); - assertSame(emptyList(), parseWebEndpointMappings("")); + assertSame(emptyList(), parseWebEndpointMappings(EMPTY_STRING)); assertSame(emptyList(), parseWebEndpointMappings(" ")); } @Test void testGetAndSetMetadata() { assertNull(getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); - setMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, this.context); + assertNull(setMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, this.context)); assertEquals(this.context, getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + assertEquals(this.context, setMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, EMPTY_STRING)); + assertEquals(EMPTY_STRING, getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); } private Collection createWebEndpointMappings() { From 7d9011fc8054fd60b570ca473104fcb71af265cf Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:58:39 +0800 Subject: [PATCH 232/310] Add removeMetadata method to ServiceInstanceUtils Introduced a static removeMetadata method to ServiceInstanceUtils for removing metadata entries from a ServiceInstance by metadata name. This utility complements existing metadata manipulation methods. --- .../client/service/util/ServiceInstanceUtils.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 1a61218f..5e259b07 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -158,6 +158,18 @@ public static String setMetadata(ServiceInstance serviceInstance, String metadat return metadata.put(metadataName, metadataValue); } + /** + * Remove metadata by metadataName + * + * @param serviceInstance {@link ServiceInstance} + * @param metadataName metadataName + * @return the value associated with metadataName if found, null otherwise + */ + public static String removeMetadata(ServiceInstance serviceInstance, String metadataName) { + Map metadata = serviceInstance.getMetadata(); + return metadata.remove(metadataName); + } + private ServiceInstanceUtils() { } } \ No newline at end of file From aacd2895efb00e0d714c69ec151c7ebff5bce17a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 19:58:45 +0800 Subject: [PATCH 233/310] Add test for removeMetadata in ServiceInstanceUtilsTest Expanded the metadata operations test to include assertions for removeMetadata, ensuring metadata removal is properly tested alongside get and set operations. --- .../cloud/client/service/util/ServiceInstanceUtilsTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 6d2477c0..0b7dc15a 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -36,6 +36,7 @@ import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMapping; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMappings; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.removeMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setMetadata; import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.SERVLET; import static io.microsphere.spring.web.metadata.WebEndpointMapping.servlet; @@ -116,12 +117,15 @@ void testParseWebEndpointMappings() { } @Test - void testGetAndSetMetadata() { + void testMetadataOps() { assertNull(getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); assertNull(setMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, this.context)); assertEquals(this.context, getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); assertEquals(this.context, setMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, EMPTY_STRING)); assertEquals(EMPTY_STRING, getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + + assertEquals(EMPTY_STRING, removeMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + assertNull(getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); } private Collection createWebEndpointMappings() { From 5b6cb15cea340b98845ed273747cb1f4534e631c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 21:10:40 +0800 Subject: [PATCH 234/310] Refactor metadata updates to use utility methods Replaced direct metadata map operations in RegistrationMetaData with setMetadata and removeMetadata utility methods for better encapsulation and consistency. --- .../cloud/client/service/registry/RegistrationMetaData.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java index 3701417e..6cab1bc0 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java @@ -10,6 +10,8 @@ import java.util.concurrent.ConcurrentHashMap; import static io.microsphere.reflect.MethodUtils.invokeMethod; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.removeMetadata; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setMetadata; import static io.microsphere.util.Assert.assertNotEmpty; import static org.springframework.aop.framework.AopProxyUtils.ultimateTargetClass; @@ -86,7 +88,7 @@ public String get(Object key) { public String put(String key, String value) { synchronized (lock) { this.registrations.forEach(registration -> { - registration.getMetadata().put(key, value); + setMetadata(registration, key, value); }); } return this.applicationMetaData.put(key, value); @@ -96,7 +98,7 @@ public String put(String key, String value) { public String remove(Object key) { synchronized (lock) { this.registrations.forEach(registration -> { - registration.getMetadata().remove(key); + removeMetadata(registration, (String) key); }); } return this.applicationMetaData.remove(key); From 71b32cb86bfe6cabccae80916e38cefa02b3de1f Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 21:10:46 +0800 Subject: [PATCH 235/310] Add utility methods for ServiceInstance URI handling Introduced getUriString and getUri methods for efficient retrieval of ServiceInstance URI representations. Refactored metadata utility methods for improved code organization. --- .../service/util/ServiceInstanceUtils.java | 113 ++++++++++++------ 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 5e259b07..2a45429f 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -24,8 +24,10 @@ import io.microsphere.spring.web.metadata.WebEndpointMapping; import io.microsphere.spring.web.metadata.WebEndpointMapping.Builder; import io.microsphere.util.BaseUtils; +import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; +import java.net.URI; import java.util.Collection; import java.util.List; import java.util.Map; @@ -33,6 +35,7 @@ import static io.microsphere.collection.ListUtils.newArrayList; import static io.microsphere.constants.SeparatorConstants.LINE_SEPARATOR; +import static io.microsphere.constants.SymbolConstants.COLON_CHAR; import static io.microsphere.constants.SymbolConstants.COMMA; import static io.microsphere.constants.SymbolConstants.LEFT_SQUARE_BRACKET; import static io.microsphere.constants.SymbolConstants.RIGHT_SQUARE_BRACKET; @@ -48,6 +51,7 @@ import static io.microsphere.util.StringUtils.EMPTY_STRING; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; import static io.microsphere.util.StringUtils.isBlank; +import static java.net.URI.create; import static java.util.Collections.emptyList; /** @@ -87,6 +91,77 @@ public static Collection getWebEndpointMappings(ServiceInsta return parseWebEndpointMappings(encodedJSON); } + /** + * Get the String representation of {@link ServiceInstance#getUri()} + * + * @param instance {@link ServiceInstance} + * @return the String representation of {@link ServiceInstance#getUri()} + */ + @Nonnull + public static String getUriString(ServiceInstance instance) { + boolean isSecure = instance.isSecure(); + String prefix = isSecure ? "https://" : "http://"; + String host = instance.getHost(); + String port = String.valueOf(instance.getPort()); + StringBuilder urlStringBuilder = new StringBuilder((isSecure ? 9 : 8) + host.length() + port.length()); + urlStringBuilder.append(prefix) + .append(host) + .append(COLON_CHAR) + .append(port); + return urlStringBuilder.toString(); + } + + /** + * Alternative method of {@link ServiceInstance#getUri()} with the better performance + * + * @param serviceInstance {@link ServiceInstance} + * @return {@link URI} instance + * @see DefaultServiceInstance#getUri(ServiceInstance) + */ + @Nonnull + public static URI getUri(ServiceInstance serviceInstance) { + String uriString = getUriString(serviceInstance); + return create(uriString); + } + + /** + * Get metadata by metadataName + * + * @param serviceInstance {@link ServiceInstance} + * @param metadataName metadataName + * @return metadata value + */ + @Nullable + public static String getMetadata(ServiceInstance serviceInstance, String metadataName) { + Map metadata = serviceInstance.getMetadata(); + return metadata.get(metadataName); + } + + /** + * Set metadata by metadataName + * + * @param serviceInstance {@link ServiceInstance} + * @param metadataName metadataName + * @param metadataValue metadata value + * @return the previous value associated with metadataName if found, null otherwise + */ + public static String setMetadata(ServiceInstance serviceInstance, String metadataName, String metadataValue) { + Map metadata = serviceInstance.getMetadata(); + return metadata.put(metadataName, metadataValue); + } + + /** + * Remove metadata by metadataName + * + * @param serviceInstance {@link ServiceInstance} + * @param metadataName metadataName + * @return the value associated with metadataName if found, null otherwise + */ + public static String removeMetadata(ServiceInstance serviceInstance, String metadataName) { + Map metadata = serviceInstance.getMetadata(); + return metadata.remove(metadataName); + } + static List parseWebEndpointMappings(String encodedJSON) { if (isBlank(encodedJSON)) { return emptyList(); @@ -132,44 +207,6 @@ static String[] getArray(JSONObject jsonObject, String name) { return jsonArray == null ? EMPTY_STRING_ARRAY : readArray(jsonArray, String.class); } - /** - * Get metadata by metadataName - * - * @param serviceInstance {@link ServiceInstance} - * @param metadataName metadataName - * @return metadata value - */ - @Nullable - public static String getMetadata(ServiceInstance serviceInstance, String metadataName) { - Map metadata = serviceInstance.getMetadata(); - return metadata.get(metadataName); - } - - /** - * Set metadata by metadataName - * - * @param serviceInstance {@link ServiceInstance} - * @param metadataName metadataName - * @param metadataValue metadata value - * @return the previous value associated with metadataName if found, null otherwise - */ - public static String setMetadata(ServiceInstance serviceInstance, String metadataName, String metadataValue) { - Map metadata = serviceInstance.getMetadata(); - return metadata.put(metadataName, metadataValue); - } - - /** - * Remove metadata by metadataName - * - * @param serviceInstance {@link ServiceInstance} - * @param metadataName metadataName - * @return the value associated with metadataName if found, null otherwise - */ - public static String removeMetadata(ServiceInstance serviceInstance, String metadataName) { - Map metadata = serviceInstance.getMetadata(); - return metadata.remove(metadataName); - } - private ServiceInstanceUtils() { } } \ No newline at end of file From bea5569fbfd4f01d2e5cc3df2a21d2d95e3ccdb2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 21:10:50 +0800 Subject: [PATCH 236/310] Add tests for getUri and getUriString methods Added unit tests for ServiceInstanceUtils.getUri and getUriString to improve coverage. Also updated testMetadataOps to consistently use 'this.serviceInstance'. --- .../util/ServiceInstanceUtilsTest.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 0b7dc15a..d2417a8d 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -33,6 +33,8 @@ import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.attachMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getMetadata; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getUri; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getUriString; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMapping; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMappings; @@ -43,6 +45,7 @@ import static io.microsphere.util.StringUtils.EMPTY_STRING; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; import static java.lang.System.currentTimeMillis; +import static java.net.URI.create; import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -116,16 +119,26 @@ void testParseWebEndpointMappings() { assertSame(emptyList(), parseWebEndpointMappings(" ")); } + @Test + void testGetUriString() { + assertEquals("http://localhost:8080", getUriString(this.serviceInstance)); + } + + @Test + void testGetUri() { + assertEquals(create("http://localhost:8080"), getUri(this.serviceInstance)); + } + @Test void testMetadataOps() { - assertNull(getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + assertNull(getMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); assertNull(setMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, this.context)); - assertEquals(this.context, getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); - assertEquals(this.context, setMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, EMPTY_STRING)); - assertEquals(EMPTY_STRING, getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + assertEquals(this.context, getMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + assertEquals(this.context, setMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME, EMPTY_STRING)); + assertEquals(EMPTY_STRING, getMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); - assertEquals(EMPTY_STRING, removeMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); - assertNull(getMetadata(serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + assertEquals(EMPTY_STRING, removeMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); + assertNull(getMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); } private Collection createWebEndpointMappings() { From 19273dc44691f5b50fa0dc3155e55188136fc2ec Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 21:26:58 +0800 Subject: [PATCH 237/310] Update ServiceInstanceUtilsTest for improved URI testing Replaces ServiceInstance with DefaultServiceInstance for the serviceInstance field and adds a test case to verify getUriString with a custom HTTPS URI. --- .../client/service/util/ServiceInstanceUtilsTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index d2417a8d..a7f94f9c 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -24,7 +24,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.cloud.client.DefaultServiceInstance; -import org.springframework.cloud.client.ServiceInstance; import java.util.Collection; @@ -70,7 +69,7 @@ public class ServiceInstanceUtilsTest { private String context = "/"; - private ServiceInstance serviceInstance; + private DefaultServiceInstance serviceInstance; private Collection webEndpointMappings; @@ -122,6 +121,10 @@ void testParseWebEndpointMappings() { @Test void testGetUriString() { assertEquals("http://localhost:8080", getUriString(this.serviceInstance)); + + String uriString = "https://localhost:8080"; + this.serviceInstance.setUri(create(uriString)); + assertEquals(uriString, getUriString(this.serviceInstance)); } @Test From bbf7df307fa0b704e99b41d5058bc1be77d33f16 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 30 Oct 2025 21:32:57 +0800 Subject: [PATCH 238/310] Update latest version numbers in README Bump the displayed latest versions for branches 0.2.x and 0.1.x in the README to 0.2.3 and 0.1.3, respectively. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 19fcb327..85609907 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ pom.xml: | **Branches** | **Purpose** | **Latest Version** | |--------------|--------------------------------------------------|--------------------| -| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.2 | -| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.2 | +| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.3 | +| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.3 | Then add the specific modules you need: From 1d808c6609cd8b82c3888cfcaf2110d68197f29e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 08:45:34 +0800 Subject: [PATCH 239/310] Bump project version to 0.2.4-SNAPSHOT Update the property in pom.xml to prepare for the next development iteration. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a43647f1..9ea2e813 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - 0.2.3-SNAPSHOT + 0.2.4-SNAPSHOT 17 From e8ff82147f072b55985e2d3288b50103d508bc3c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 08:47:00 +0800 Subject: [PATCH 240/310] Add constants for reactive discovery client auto configs Introduced constants for SimpleReactiveDiscoveryClientAutoConfiguration and ReactiveCompositeDiscoveryClientAutoConfiguration class names in DiscoveryClientConstants. This improves maintainability and consistency when referencing these class names. --- .../constants/DiscoveryClientConstants.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java index 505c5a9d..5cce42f7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstants.java @@ -20,6 +20,8 @@ import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient; +import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration; /** * The constants for {@link DiscoveryClient} @@ -56,4 +58,18 @@ public interface DiscoveryClientConstants { * @see org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration */ String REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration"; + + /** + * The class name of {@link SimpleReactiveDiscoveryClientAutoConfiguration} + * + * @see org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration + */ + String SIMPLE_REACTIVE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration"; + + /** + * The class name of {@link ReactiveCompositeDiscoveryClientAutoConfiguration} + * + * @see org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration + */ + String REACTIVE_COMPOSITE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration"; } \ No newline at end of file From e9c4aba3f853ff33144ef7968d4794940c59ddc1 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 08:47:05 +0800 Subject: [PATCH 241/310] Add tests for new reactive discovery client constants Added assertions for SIMPLE_REACTIVE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME and REACTIVE_COMPOSITE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME in DiscoveryClientConstantsTest to improve test coverage for recently introduced constants. --- .../discovery/constants/DiscoveryClientConstantsTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java index b92ae186..131a2c1e 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/constants/DiscoveryClientConstantsTest.java @@ -22,6 +22,8 @@ import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.REACTIVE_COMPOSITE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.SIMPLE_REACTIVE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -39,5 +41,7 @@ void testConstants() { assertEquals("org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient", COMPOSITE_DISCOVERY_CLIENT_CLASS_NAME); assertEquals("org.springframework.cloud.client.CommonsClientAutoConfiguration", COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); assertEquals("org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration", REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); + assertEquals("org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration", SIMPLE_REACTIVE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); + assertEquals("org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration", REACTIVE_COMPOSITE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME); } } \ No newline at end of file From 838b9f24e845d907cd09b0b286d39258f546e39e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 08:47:11 +0800 Subject: [PATCH 242/310] Add @AutoConfigureAfter to ReactiveDiscoveryClientAutoConfiguration Annotated ReactiveDiscoveryClientAutoConfiguration with @AutoConfigureAfter to ensure it is configured after SimpleReactiveDiscoveryClientAutoConfiguration and ReactiveCompositeDiscoveryClientAutoConfiguration. This improves the ordering of auto-configuration classes. --- .../ReactiveDiscoveryClientAutoConfiguration.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java index ec164f29..26aa7b18 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java @@ -18,6 +18,7 @@ package io.microsphere.spring.cloud.client.discovery.autoconfigure; import io.microsphere.spring.cloud.client.discovery.ReactiveDiscoveryClientAdapter; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -30,6 +31,8 @@ import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.DISCOVERY_CLIENT_CLASS_NAME; import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.REACTIVE_COMPOSITE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; +import static io.microsphere.spring.cloud.client.discovery.constants.DiscoveryClientConstants.SIMPLE_REACTIVE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; /** * The Auto-Configuration class for {@link ReactiveDiscoveryClient} @@ -48,6 +51,10 @@ @AutoConfigureBefore(name = { REACTIVE_COMMONS_CLIENT_AUTO_CONFIGURATION_CLASS_NAME }) +@AutoConfigureAfter(name = { + SIMPLE_REACTIVE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME, + REACTIVE_COMPOSITE_DISCOVERY_CLIENT_AUTO_CONFIGURATION_CLASS_NAME +}) public class ReactiveDiscoveryClientAutoConfiguration { @Configuration(proxyBeanMethods = false) From 608b8f284871b50e389300817a10d60a2193daa5 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 09:54:24 +0800 Subject: [PATCH 243/310] Add DiscoveryUtils utility class for service discovery Introduces DiscoveryUtils, a utility class providing helper methods for working with SimpleDiscoveryProperties and SimpleReactiveDiscoveryProperties in Spring Cloud. Includes methods for retrieving instance maps and converting between the two property types. --- .../client/discovery/util/DiscoveryUtils.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java new file mode 100644 index 00000000..dc1dd9ff --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.discovery.util; + +import io.microsphere.annotation.Nonnull; +import io.microsphere.util.Utils; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryProperties; + +import java.util.List; +import java.util.Map; + +import static io.microsphere.reflect.MethodUtils.invokeMethod; + +/** + * The utilities class for Spring Cloud Discovery + * + * @author Mercy + * @see Utils + * @since 1.0.0 + */ +public abstract class DiscoveryUtils implements Utils { + + /** + * Get the instances map from {@link SimpleDiscoveryProperties} + * + * @param properties {@link SimpleDiscoveryProperties} + * @return the instances map + */ + @Nonnull + public static Map> getInstancesMap(SimpleDiscoveryProperties properties) { + return properties.getInstances(); + } + + /** + * Get the instances map from {@link SimpleReactiveDiscoveryProperties} + * + * @param properties {@link SimpleReactiveDiscoveryProperties} + * @return the instances map + */ + @Nonnull + public static Map> getInstancesMap(SimpleReactiveDiscoveryProperties properties) { + return invokeMethod(properties, "getInstances"); + } + + public static SimpleReactiveDiscoveryProperties simpleReactiveDiscoveryProperties(@Nonnull SimpleDiscoveryProperties properties) { + SimpleReactiveDiscoveryProperties simpleReactiveDiscoveryProperties = new SimpleReactiveDiscoveryProperties(); + simpleReactiveDiscoveryProperties.setOrder(properties.getOrder()); + + DefaultServiceInstance local = properties.getLocal(); + DefaultServiceInstance targetLocal = simpleReactiveDiscoveryProperties.getLocal(); + + simpleReactiveDiscoveryProperties.setInstances(getInstancesMap(properties)); + + simpleReactiveDiscoveryProperties.afterPropertiesSet(); + + return simpleReactiveDiscoveryProperties; + } + + /** + * Convert {@link SimpleReactiveDiscoveryProperties} to {@link SimpleDiscoveryProperties} + * + * @param properties {@link SimpleReactiveDiscoveryProperties} + * @return {@link SimpleDiscoveryProperties} + */ + @Nonnull + public static SimpleDiscoveryProperties simpleDiscoveryProperties(@Nonnull SimpleReactiveDiscoveryProperties properties) { + SimpleDiscoveryProperties simpleDiscoveryProperties = new SimpleDiscoveryProperties(); + simpleDiscoveryProperties.setOrder(properties.getOrder()); + + DefaultServiceInstance local = properties.getLocal(); + simpleDiscoveryProperties.setInstance(local.getServiceId(), local.getHost(), local.getPort()); + + Map> instances = invokeMethod(properties, "getInstances"); + simpleDiscoveryProperties.setInstances(instances); + + simpleDiscoveryProperties.afterPropertiesSet(); + + return simpleDiscoveryProperties; + } + + private DiscoveryUtils() { + } +} From dca8d66d467d64403adde308e4372796d5d42c98 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 09:54:28 +0800 Subject: [PATCH 244/310] Add unit tests for DiscoveryUtils utility class Introduces DiscoveryUtilsTest to verify the behavior of DiscoveryUtils methods, including getInstancesMap and simpleDiscoveryProperties, using both SimpleDiscoveryProperties and SimpleReactiveDiscoveryProperties. --- .../discovery/util/DiscoveryUtilsTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtilsTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtilsTest.java new file mode 100644 index 00000000..c795655a --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtilsTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.client.discovery.util; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryProperties; + +import java.util.List; +import java.util.Map; + +import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.spring.cloud.client.discovery.util.DiscoveryUtils.getInstancesMap; +import static io.microsphere.spring.cloud.client.discovery.util.DiscoveryUtils.simpleDiscoveryProperties; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtilsTest.createDefaultServiceInstance; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link DiscoveryUtils} Test + * + * @author Mercy + * @see DiscoveryUtils + * @since 1.0.0 + */ +class DiscoveryUtilsTest { + + private DefaultServiceInstance serviceInstance; + + private SimpleDiscoveryProperties properties; + + @BeforeEach + void setUp() { + this.serviceInstance = createDefaultServiceInstance(); + this.properties = new SimpleDiscoveryProperties(); + Map> instancesMap = this.properties.getInstances(); + instancesMap.put(this.serviceInstance.getInstanceId(), ofList(this.serviceInstance)); + } + + @Test + void testGetInstancesMap() { + Map> instancesMap = getInstancesMap(this.properties); + assertEquals(1, instancesMap.size()); + assertEquals(ofList(this.serviceInstance), instancesMap.get(this.serviceInstance.getInstanceId())); + } + + @Test + void testGetInstancesMapFromSimpleReactiveDiscoveryProperties() { + SimpleReactiveDiscoveryProperties properties = new SimpleReactiveDiscoveryProperties(); + Map> instancesMap = getInstancesMap(properties); + assertTrue(instancesMap.isEmpty()); + + properties.setInstances(this.properties.getInstances()); + instancesMap = getInstancesMap(properties); + assertEquals(1, instancesMap.size()); + assertEquals(ofList(this.serviceInstance), instancesMap.get(this.serviceInstance.getInstanceId())); + } + + @Test + void testSimpleDiscoveryProperties() { + SimpleReactiveDiscoveryProperties properties = new SimpleReactiveDiscoveryProperties(); + properties.setInstances(this.properties.getInstances()); + + SimpleDiscoveryProperties simpleDiscoveryProperties = simpleDiscoveryProperties(properties); + assertEquals(this.properties.getInstances(), simpleDiscoveryProperties.getInstances()); + } +} \ No newline at end of file From 4cdf936bbafb7c043cae08390442e4a331ad862a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 10:00:00 +0800 Subject: [PATCH 245/310] Add support for SimpleReactiveDiscoveryProperties Extended SimpleServiceRegistry to support both SimpleDiscoveryProperties and SimpleReactiveDiscoveryProperties by overloading constructors and using a utility method to obtain the instances map. This enhances compatibility with reactive discovery configurations. --- .../registry/SimpleServiceRegistry.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java index 262a4e05..6d90e12b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java @@ -19,23 +19,27 @@ import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryProperties; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import java.util.ArrayList; import java.util.List; import java.util.Map; +import static io.microsphere.spring.cloud.client.discovery.util.DiscoveryUtils.getInstancesMap; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setMetadata; /** - * Simple {@link ServiceRegistry} class that is based on {@link SimpleDiscoveryProperties} to register + * Simple {@link ServiceRegistry} class that is based on {@link SimpleDiscoveryProperties} + * or {@link SimpleReactiveDiscoveryProperties} to register * {@link DefaultRegistration}. * * @author Mercy * @see ServiceRegistry * @see DefaultRegistration * @see SimpleDiscoveryProperties#getInstances() + * @see SimpleReactiveDiscoveryProperties#getInstances() * @since 1.0.0 */ public class SimpleServiceRegistry implements ServiceRegistry { @@ -44,8 +48,16 @@ public class SimpleServiceRegistry implements ServiceRegistry> instancesMap; - public SimpleServiceRegistry(SimpleDiscoveryProperties simpleDiscoveryProperties) { - this.instancesMap = simpleDiscoveryProperties.getInstances(); + public SimpleServiceRegistry(SimpleDiscoveryProperties properties) { + this(getInstancesMap(properties)); + } + + public SimpleServiceRegistry(SimpleReactiveDiscoveryProperties properties) { + this(getInstancesMap(properties)); + } + + public SimpleServiceRegistry(Map> instancesMap) { + this.instancesMap = instancesMap; } @Override From 40ab6b87d81c4e788e22e23d4527d995f340f197 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 10:00:07 +0800 Subject: [PATCH 246/310] Add constructor test for SimpleServiceRegistry Introduces a test for the SimpleServiceRegistry constructor that accepts SimpleReactiveDiscoveryProperties. Also refactors testGetStatus to use testSetStatus for improved clarity. --- .../registry/SimpleServiceRegistryTest.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java index 028a8781..1b11e19e 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistryTest.java @@ -22,10 +22,12 @@ import org.junit.jupiter.api.Test; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryProperties; import java.util.List; import java.util.Map; +import static io.microsphere.spring.cloud.client.discovery.util.DiscoveryUtils.simpleReactiveDiscoveryProperties; import static io.microsphere.spring.cloud.client.service.registry.DefaultRegistrationTest.createDefaultRegistration; import static io.microsphere.spring.cloud.client.service.registry.SimpleServiceRegistry.STATUS_KEY; import static java.util.Collections.emptyList; @@ -55,6 +57,13 @@ void setUp() { this.registry = new SimpleServiceRegistry(this.properties); } + @Test + void testConstructor() { + SimpleReactiveDiscoveryProperties properties = simpleReactiveDiscoveryProperties(this.properties); + this.registry = new SimpleServiceRegistry(properties); + testDeregister(); + } + @Test void testRegister() { Map> instancesMap = this.properties.getInstances(); @@ -94,10 +103,8 @@ void testSetStatus() { @Test void testGetStatus() { - testRegister(); - String status = "UP"; - this.registry.setStatus(this.registration, status); - assertEquals(status, this.registry.getStatus(this.registration)); + testSetStatus(); + assertEquals(this.registration.getMetadata().get(STATUS_KEY), this.registry.getStatus(this.registration)); } List getInstances(String serviceId) { From da39278a78b1e5c9d97645a3775bbf5fc3269da0 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 10:09:04 +0800 Subject: [PATCH 247/310] Add setProperties method to ServiceInstanceUtils Introduced a utility method to copy properties from a ServiceInstance to a DefaultServiceInstance, streamlining property assignment between these types. --- .../service/util/ServiceInstanceUtils.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 2a45429f..39143d4e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -162,6 +162,23 @@ public static String removeMetadata(ServiceInstance serviceInstance, String meta return metadata.remove(metadataName); } + /** + * Set properties from source to target + * + * @param source source {@link ServiceInstance} + * @param target target {@link DefaultServiceInstance} + */ + public static void setProperties(ServiceInstance source, DefaultServiceInstance target) { + target.setInstanceId(source.getInstanceId()); + target.setServiceId(source.getServiceId()); + target.setHost(source.getHost()); + target.setPort(source.getPort()); + target.setSecure(source.isSecure()); + Map metadata = source.getMetadata(); + metadata.clear(); + metadata.putAll(source.getMetadata()); + } + static List parseWebEndpointMappings(String encodedJSON) { if (isBlank(encodedJSON)) { return emptyList(); From 91605f76df3ce5884ff4eedf670f07dd9dd89d3a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 10:09:09 +0800 Subject: [PATCH 248/310] Add test for setProperties in ServiceInstanceUtilsTest Introduced a new unit test to verify the setProperties method, ensuring that properties are correctly copied between service instances. --- .../client/service/util/ServiceInstanceUtilsTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index a7f94f9c..9c0c9439 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -39,6 +39,7 @@ import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.parseWebEndpointMappings; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.removeMetadata; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setMetadata; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setProperties; import static io.microsphere.spring.web.metadata.WebEndpointMapping.Kind.SERVLET; import static io.microsphere.spring.web.metadata.WebEndpointMapping.servlet; import static io.microsphere.util.StringUtils.EMPTY_STRING; @@ -144,6 +145,13 @@ void testMetadataOps() { assertNull(getMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME)); } + @Test + void testSetProperties() { + DefaultServiceInstance target = new DefaultServiceInstance(); + setProperties(this.serviceInstance, target); + assertEquals(this.serviceInstance, target); + } + private Collection createWebEndpointMappings() { return ofList(buildWebEndpointMapping(true)); } From d4ccf71853c0f134e0d701132854db7450cfc623 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 10:25:17 +0800 Subject: [PATCH 249/310] Set URI directly in setProperties method Updated ServiceInstanceUtils.setProperties to set the URI on the target DefaultServiceInstance using getUri(source) instead of setting host, port, and secure individually. --- .../cloud/client/service/util/ServiceInstanceUtils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 39143d4e..3cc7db46 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -171,9 +171,8 @@ public static String removeMetadata(ServiceInstance serviceInstance, String meta public static void setProperties(ServiceInstance source, DefaultServiceInstance target) { target.setInstanceId(source.getInstanceId()); target.setServiceId(source.getServiceId()); - target.setHost(source.getHost()); - target.setPort(source.getPort()); - target.setSecure(source.isSecure()); + URI uri = getUri(source); + target.setUri(uri); Map metadata = source.getMetadata(); metadata.clear(); metadata.putAll(source.getMetadata()); From dc714ce1edf1298b15c1aa5b4a2c70c9f4b643dc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 10:31:31 +0800 Subject: [PATCH 250/310] Enhance DiscoveryUtils with property copying and null checks Added property copying for DefaultServiceInstance when converting SimpleDiscoveryProperties to SimpleReactiveDiscoveryProperties using setProperties. Also added @Nonnull annotations to method parameters for better null safety. --- .../client/discovery/util/DiscoveryUtils.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java index dc1dd9ff..db3a521f 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/util/DiscoveryUtils.java @@ -27,6 +27,7 @@ import java.util.Map; import static io.microsphere.reflect.MethodUtils.invokeMethod; +import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.setProperties; /** * The utilities class for Spring Cloud Discovery @@ -44,7 +45,7 @@ public abstract class DiscoveryUtils implements Utils { * @return the instances map */ @Nonnull - public static Map> getInstancesMap(SimpleDiscoveryProperties properties) { + public static Map> getInstancesMap(@Nonnull SimpleDiscoveryProperties properties) { return properties.getInstances(); } @@ -55,20 +56,27 @@ public static Map> getInstancesMap(SimpleDi * @return the instances map */ @Nonnull - public static Map> getInstancesMap(SimpleReactiveDiscoveryProperties properties) { + public static Map> getInstancesMap(@Nonnull SimpleReactiveDiscoveryProperties properties) { return invokeMethod(properties, "getInstances"); } + /** + * Convert {@link SimpleDiscoveryProperties} to {@link SimpleReactiveDiscoveryProperties} + * + * @param properties {@link SimpleDiscoveryProperties} + * @return {@link SimpleReactiveDiscoveryProperties} + */ + @Nonnull public static SimpleReactiveDiscoveryProperties simpleReactiveDiscoveryProperties(@Nonnull SimpleDiscoveryProperties properties) { SimpleReactiveDiscoveryProperties simpleReactiveDiscoveryProperties = new SimpleReactiveDiscoveryProperties(); simpleReactiveDiscoveryProperties.setOrder(properties.getOrder()); DefaultServiceInstance local = properties.getLocal(); DefaultServiceInstance targetLocal = simpleReactiveDiscoveryProperties.getLocal(); + setProperties(targetLocal, local); - simpleReactiveDiscoveryProperties.setInstances(getInstancesMap(properties)); - - simpleReactiveDiscoveryProperties.afterPropertiesSet(); + Map> instances = getInstancesMap(properties); + simpleReactiveDiscoveryProperties.setInstances(instances); return simpleReactiveDiscoveryProperties; } @@ -90,11 +98,9 @@ public static SimpleDiscoveryProperties simpleDiscoveryProperties(@Nonnull Simpl Map> instances = invokeMethod(properties, "getInstances"); simpleDiscoveryProperties.setInstances(instances); - simpleDiscoveryProperties.afterPropertiesSet(); - return simpleDiscoveryProperties; } private DiscoveryUtils() { } -} +} \ No newline at end of file From 7935f59a000c4f831767cf3abfa6dfa146465fd3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 11:01:33 +0800 Subject: [PATCH 251/310] Default port in URL if ServiceInstance port is invalid Update ServiceInstanceUtils to use port 443 for secure instances and 80 for non-secure instances when the provided port is less than or equal to zero. This ensures generated URLs are always valid even if the ServiceInstance port is not set. --- .../client/service/util/ServiceInstanceUtils.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 3cc7db46..d8fa4063 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -51,6 +51,7 @@ import static io.microsphere.util.StringUtils.EMPTY_STRING; import static io.microsphere.util.StringUtils.EMPTY_STRING_ARRAY; import static io.microsphere.util.StringUtils.isBlank; +import static java.lang.String.valueOf; import static java.net.URI.create; import static java.util.Collections.emptyList; @@ -102,12 +103,16 @@ public static String getUriString(ServiceInstance instance) { boolean isSecure = instance.isSecure(); String prefix = isSecure ? "https://" : "http://"; String host = instance.getHost(); - String port = String.valueOf(instance.getPort()); - StringBuilder urlStringBuilder = new StringBuilder((isSecure ? 9 : 8) + host.length() + port.length()); + int port = instance.getPort(); + if (port <= 0) { + port = isSecure ? 443 : 80; + } + String portString = valueOf(port); + StringBuilder urlStringBuilder = new StringBuilder((isSecure ? 9 : 8) + host.length() + portString.length()); urlStringBuilder.append(prefix) .append(host) .append(COLON_CHAR) - .append(port); + .append(portString); return urlStringBuilder.toString(); } From bcd555f22c48cecd6a48a374bd3633570b212630 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 11:01:38 +0800 Subject: [PATCH 252/310] Add tests for getUriString and getUri without port Added test cases to verify getUriString and getUri methods handle URIs without explicit ports, ensuring default ports (80 for HTTP, 443 for HTTPS) are appended as expected. --- .../util/ServiceInstanceUtilsTest.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 9c0c9439..81b04685 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.cloud.client.DefaultServiceInstance; +import java.net.URI; import java.util.Collection; import static io.microsphere.collection.Lists.ofList; @@ -126,11 +127,31 @@ void testGetUriString() { String uriString = "https://localhost:8080"; this.serviceInstance.setUri(create(uriString)); assertEquals(uriString, getUriString(this.serviceInstance)); + + } + + @Test + void testGetUriStringWithoutPort() { + String uriString = "http://localhost"; + this.serviceInstance.setUri(create(uriString)); + assertEquals(uriString + ":80", getUriString(this.serviceInstance)); + + uriString = "https://localhost"; + this.serviceInstance.setUri(create(uriString)); + assertEquals(uriString + ":443", getUriString(this.serviceInstance)); } @Test void testGetUri() { - assertEquals(create("http://localhost:8080"), getUri(this.serviceInstance)); + URI uri = getUri(this.serviceInstance); + assertEquals(create("http://localhost:8080"), uri); + assertEquals(DefaultServiceInstance.getUri(this.serviceInstance), uri); + + uri = create("https://localhost"); + this.serviceInstance.setUri(uri); + uri = getUri(this.serviceInstance); + assertEquals(create("https://localhost:443"), uri); + assertEquals(DefaultServiceInstance.getUri(this.serviceInstance), uri); } @Test From 75aa9017f2f98e8fdce42514b91037635f2443e2 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 11:13:40 +0800 Subject: [PATCH 253/310] Update property mapping in setProperties method Replaces setting URI on target with setting secure, host, and port properties from the source ServiceInstance. This change ensures that DefaultServiceInstance receives these individual properties directly. --- .../cloud/client/service/util/ServiceInstanceUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index d8fa4063..71aab2bf 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -176,8 +176,9 @@ public static String removeMetadata(ServiceInstance serviceInstance, String meta public static void setProperties(ServiceInstance source, DefaultServiceInstance target) { target.setInstanceId(source.getInstanceId()); target.setServiceId(source.getServiceId()); - URI uri = getUri(source); - target.setUri(uri); + target.setSecure(source.isSecure()); + target.setHost(source.getHost()); + target.setPort(source.getPort()); Map metadata = source.getMetadata(); metadata.clear(); metadata.putAll(source.getMetadata()); From 9e3928d53db69f6e851c07e81c26b77ce9dca635 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 11:13:46 +0800 Subject: [PATCH 254/310] Remove unnecessary blank line in ServiceInstanceUtilsTest Cleaned up formatting by deleting an extra blank line in the getUriString test method. --- .../cloud/client/service/util/ServiceInstanceUtilsTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java index 81b04685..ef194316 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java @@ -127,7 +127,6 @@ void testGetUriString() { String uriString = "https://localhost:8080"; this.serviceInstance.setUri(create(uriString)); assertEquals(uriString, getUriString(this.serviceInstance)); - } @Test From e8920dd279eaee2231c651be738a7ff2b77a7e54 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 11:52:25 +0800 Subject: [PATCH 255/310] Update pom.xml --- microsphere-spring-cloud-commons/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/microsphere-spring-cloud-commons/pom.xml b/microsphere-spring-cloud-commons/pom.xml index f9869aca..be5392c8 100644 --- a/microsphere-spring-cloud-commons/pom.xml +++ b/microsphere-spring-cloud-commons/pom.xml @@ -67,6 +67,11 @@ true + + org.springframework.cloud + spring-cloud-loadbalancer + true + From cc321e8d3dda35aa90b0699ee712b25c2e4a98af Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 11:52:33 +0800 Subject: [PATCH 256/310] Add constant for load balancer enabled property Introduced LOAD_BALANCER_ENABLED_PROPERTY_NAME to represent the 'spring.cloud.loadbalancer.enabled' property, including relevant documentation and configuration property annotation. --- .../constants/SpringCloudPropertyConstants.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java index bcabfd50..9af29f2a 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java @@ -68,4 +68,17 @@ public interface SpringCloudPropertyConstants { source = APPLICATION_SOURCE ) String FEATURES_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "features." + ENABLED_PROPERTY_NAME; + + + /** + * The property name for enabling Spring Cloud Load-Balancer : "spring.cloud.loadbalancer.enabled" + * + * @see org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration + */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "true", + source = APPLICATION_SOURCE + ) + String LOAD_BALANCER_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "loadbalancer." + ENABLED_PROPERTY_NAME; } \ No newline at end of file From 55195f3aa318da9cf553fe4b19b4df061bdc3751 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 11:52:38 +0800 Subject: [PATCH 257/310] Add unit test for SpringCloudPropertyConstants Introduces SpringCloudPropertyConstantsTest to verify the values of property name constants in SpringCloudPropertyConstants. --- .../SpringCloudPropertyConstantsTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.java new file mode 100644 index 00000000..aea36747 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.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 io.microsphere.spring.cloud.commons.constants; + + +import org.junit.jupiter.api.Test; + +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.FEATURES_ENABLED_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.LOAD_BALANCER_ENABLED_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME; +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.SERVICE_REGISTRY_PROPERTY_PREFIX; +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.SPRING_CLOUD_PROPERTY_PREFIX; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link SpringCloudPropertyConstants} Test + * + * @author Mercy + * @see SpringCloudPropertyConstants + * @since 1.0.0 + */ +class SpringCloudPropertyConstantsTest { + + @Test + void testConstants() { + assertEquals("spring.cloud.", SPRING_CLOUD_PROPERTY_PREFIX); + assertEquals("spring.cloud.service-registry.", SERVICE_REGISTRY_PROPERTY_PREFIX); + assertEquals("spring.cloud.service-registry.auto-registration.enabled", SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME); + assertEquals("spring.cloud.features.enabled", FEATURES_ENABLED_PROPERTY_NAME); + assertEquals("spring.cloud.loadbalancer.enabled", LOAD_BALANCER_ENABLED_PROPERTY_NAME); + } +} \ No newline at end of file From f49d2e45e411b0c476fbbce14037d347bce82e17 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 12:15:56 +0800 Subject: [PATCH 258/310] Add ConditionalOnLoadBalancerEnabled annotation Introduces a custom annotation that meta-annotates @ConditionalOnProperty for enabling LoadBalancer features based on the 'loadbalancer.enabled' property. This simplifies conditional bean registration for LoadBalancer-related components. --- .../ConditionalOnLoadBalancerEnabled.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabled.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabled.java new file mode 100644 index 00000000..5b04c06e --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabled.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.microsphere.spring.cloud.loadbalancer.condition; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.LOAD_BALANCER_ENABLED_PROPERTY_NAME; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * The conditional annotation meta-annotates {@link ConditionalOnProperty @ConditionalOnProperty} for + * LoadBalancer enabled. + * + * @author Mercy + * @see org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration + * @see ConditionalOnProperty + * @since 1.0.0 + */ +@Retention(RUNTIME) +@Target({TYPE, METHOD}) +@Documented +@ConditionalOnProperty(name = LOAD_BALANCER_ENABLED_PROPERTY_NAME, havingValue = "true", matchIfMissing = true) +public @interface ConditionalOnLoadBalancerEnabled { +} \ No newline at end of file From 4a73fd386f4d251b529ff4b03e54f5312e1391f9 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 12:16:00 +0800 Subject: [PATCH 259/310] Add test for ConditionalOnLoadBalancerEnabled Introduces a unit test to verify the behavior of the @ConditionalOnLoadBalancerEnabled annotation, ensuring the Config bean is conditionally present based on the 'spring.cloud.loadbalancer.enabled' property. --- .../ConditionalOnLoadBalancerEnabledTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java new file mode 100644 index 00000000..6a87e4aa --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.loadbalancer.condition; + +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +import static com.alibaba.spring.util.BeanUtils.isBeanPresent; +import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; +import static java.lang.System.getProperties; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link ConditionalOnLoadBalancerEnabled @ConditionalOnLoadBalancerEnabled} Test + * + * @author Mercy + * @see ConditionalOnLoadBalancerEnabled + * @since 1.0.0 + */ +public class ConditionalOnLoadBalancerEnabledTest { + + @ConditionalOnLoadBalancerEnabled + static class Config { + } + + @Test + void testConfigBeanPresent() { + testConfigBean(true); + } + + @Test + void testConfigBeanAbsent() { + Properties properties = getProperties(); + try { + properties.setProperty("spring.cloud.loadbalancer.enabled", "false"); + testConfigBean(false); + } finally { + properties.remove("spring.cloud.loadbalancer.enabled"); + } + } + + void testConfigBean(boolean present) { + testInSpringContainer(context -> { + assertEquals(present, isBeanPresent(context, Config.class)); + }, Config.class); + } +} From 2d51c53437dd032469b79647fadc33862d3da174 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:12:13 +0800 Subject: [PATCH 260/310] Add property constant for Spring Cloud Util enablement Introduced UTIL_ENABLED_PROPERTY_NAME for enabling or disabling Spring Cloud Util via the 'spring.cloud.util.enabled' property. This addition aligns with the existing pattern for feature toggles and references UtilAutoConfiguration. --- .../constants/SpringCloudPropertyConstants.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java index 9af29f2a..0fce3f30 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstants.java @@ -20,6 +20,7 @@ import io.microsphere.annotation.ConfigurationProperty; import org.springframework.cloud.client.CommonsClientAutoConfiguration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; +import org.springframework.cloud.commons.util.UtilAutoConfiguration; import static io.microsphere.annotation.ConfigurationProperty.APPLICATION_SOURCE; import static io.microsphere.constants.PropertyConstants.ENABLED_PROPERTY_NAME; @@ -69,7 +70,6 @@ public interface SpringCloudPropertyConstants { ) String FEATURES_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "features." + ENABLED_PROPERTY_NAME; - /** * The property name for enabling Spring Cloud Load-Balancer : "spring.cloud.loadbalancer.enabled" * @@ -81,4 +81,17 @@ public interface SpringCloudPropertyConstants { source = APPLICATION_SOURCE ) String LOAD_BALANCER_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "loadbalancer." + ENABLED_PROPERTY_NAME; + + + /** + * The property name for enabling Spring Cloud Util : "spring.cloud.util.enabled" + * + * @see UtilAutoConfiguration + */ + @ConfigurationProperty( + type = boolean.class, + defaultValue = "true", + source = APPLICATION_SOURCE + ) + String UTIL_ENABLED_PROPERTY_NAME = SPRING_CLOUD_PROPERTY_PREFIX + "util." + ENABLED_PROPERTY_NAME; } \ No newline at end of file From 28df5846c0e781694a53bd23b1fc8122eafa901d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:12:23 +0800 Subject: [PATCH 261/310] Add test for UTIL_ENABLED_PROPERTY_NAME constant Added an assertion to verify the value of UTIL_ENABLED_PROPERTY_NAME in SpringCloudPropertyConstantsTest to ensure the constant is correctly defined. --- .../commons/constants/SpringCloudPropertyConstantsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.java index aea36747..41b9a193 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/constants/SpringCloudPropertyConstantsTest.java @@ -25,6 +25,7 @@ import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME; import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.SERVICE_REGISTRY_PROPERTY_PREFIX; import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.SPRING_CLOUD_PROPERTY_PREFIX; +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.UTIL_ENABLED_PROPERTY_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -43,5 +44,6 @@ void testConstants() { assertEquals("spring.cloud.service-registry.auto-registration.enabled", SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED_PROPERTY_NAME); assertEquals("spring.cloud.features.enabled", FEATURES_ENABLED_PROPERTY_NAME); assertEquals("spring.cloud.loadbalancer.enabled", LOAD_BALANCER_ENABLED_PROPERTY_NAME); + assertEquals("spring.cloud.util.enabled", UTIL_ENABLED_PROPERTY_NAME); } } \ No newline at end of file From adf8b37233dfbfb087793df94edf29ecd41e121c Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:12:32 +0800 Subject: [PATCH 262/310] Refactor ConditionalOnLoadBalancerEnabledTest class usage Moved the @ConditionalOnLoadBalancerEnabled annotation from the inner Config class to the test class itself. Updated testConfigBean to reference the test class instead of the inner Config class, simplifying the test structure. --- .../condition/ConditionalOnLoadBalancerEnabledTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java index 6a87e4aa..d794f309 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java @@ -33,12 +33,9 @@ * @see ConditionalOnLoadBalancerEnabled * @since 1.0.0 */ +@ConditionalOnLoadBalancerEnabled public class ConditionalOnLoadBalancerEnabledTest { - @ConditionalOnLoadBalancerEnabled - static class Config { - } - @Test void testConfigBeanPresent() { testConfigBean(true); @@ -57,7 +54,7 @@ void testConfigBeanAbsent() { void testConfigBean(boolean present) { testInSpringContainer(context -> { - assertEquals(present, isBeanPresent(context, Config.class)); - }, Config.class); + assertEquals(present, isBeanPresent(context, getClass())); + }, getClass()); } } From 348a220dd2485e3488939e62f42afc730dea041e Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:13:10 +0800 Subject: [PATCH 263/310] Add ConditionalOnUtilEnabled annotation Introduces a custom annotation that meta-annotates @ConditionalOnProperty for UtilAutoConfiguration, enabling conditional configuration based on the UTIL_ENABLED_PROPERTY_NAME property. --- .../condition/ConditionalOnUtilEnabled.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabled.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabled.java new file mode 100644 index 00000000..a24ed4cb --- /dev/null +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabled.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 io.microsphere.spring.cloud.commons.condition; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.commons.util.UtilAutoConfiguration; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static io.microsphere.spring.cloud.commons.constants.SpringCloudPropertyConstants.UTIL_ENABLED_PROPERTY_NAME; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * The conditional annotation meta-annotates {@link ConditionalOnProperty @ConditionalOnProperty} for + * {@link UtilAutoConfiguration} enabled. + * + * @author Mercy + * @see UtilAutoConfiguration + * @see ConditionalOnProperty + * @since 1.0.0 + */ +@Retention(RUNTIME) +@Target({TYPE, METHOD}) +@Documented +@ConditionalOnProperty(name = UTIL_ENABLED_PROPERTY_NAME, matchIfMissing = true) +public @interface ConditionalOnUtilEnabled { +} \ No newline at end of file From 07a6b8bd6a164ed5470f38b31c892c0ad1b59e8b Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:13:16 +0800 Subject: [PATCH 264/310] Add test for ConditionalOnUtilEnabled annotation Introduces ConditionalOnUtilEnabledTest to verify bean presence based on the 'spring.cloud.util.enabled' property. Ensures correct conditional behavior in Spring container. --- .../ConditionalOnUtilEnabledTest.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java new file mode 100644 index 00000000..74bd864a --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.microsphere.spring.cloud.commons.condition; + +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +import static com.alibaba.spring.util.BeanUtils.isBeanPresent; +import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; +import static java.lang.System.getProperties; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link ConditionalOnUtilEnabled} Test + * + * @author Mercy + * @see ConditionalOnUtilEnabled + * @since 1.0.0 + */ +@ConditionalOnUtilEnabled +public class ConditionalOnUtilEnabledTest { + + @Test + void testConfigBeanPresent() { + testConfigBean(true); + } + + @Test + void testConfigBeanAbsent() { + Properties properties = getProperties(); + try { + properties.setProperty("spring.cloud.util.enabled", "false"); + testConfigBean(false); + } finally { + properties.remove("spring.cloud.util.enabled"); + } + } + + void testConfigBean(boolean present) { + testInSpringContainer(context -> { + assertEquals(present, isBeanPresent(context, getClass())); + }, getClass()); + } +} From 1ae89291692b918dffd19fbe6f015563c80cbe24 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:23:18 +0800 Subject: [PATCH 265/310] Fix test class reference in condition test cases Replaces usage of getClass() with explicit test class references in ConditionalOnUtilEnabledTest and ConditionalOnLoadBalancerEnabledTest to ensure correct bean presence checks. --- .../cloud/commons/condition/ConditionalOnUtilEnabledTest.java | 4 ++-- .../condition/ConditionalOnLoadBalancerEnabledTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java index 74bd864a..f92cb37c 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java @@ -54,7 +54,7 @@ void testConfigBeanAbsent() { void testConfigBean(boolean present) { testInSpringContainer(context -> { - assertEquals(present, isBeanPresent(context, getClass())); - }, getClass()); + assertEquals(present, isBeanPresent(context, ConditionalOnUtilEnabledTest.class)); + }, ConditionalOnUtilEnabledTest.class); } } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java index d794f309..959565a4 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java @@ -54,7 +54,7 @@ void testConfigBeanAbsent() { void testConfigBean(boolean present) { testInSpringContainer(context -> { - assertEquals(present, isBeanPresent(context, getClass())); - }, getClass()); + assertEquals(present, isBeanPresent(context, ConditionalOnLoadBalancerEnabledTest.class)); + }, ConditionalOnLoadBalancerEnabledTest.class); } } From 37cb99c3eb1a4d152a53ee9aafe97eca58a01a31 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:23:35 +0800 Subject: [PATCH 266/310] Update ConditionalOnUtilEnabledTest.java --- .../cloud/commons/condition/ConditionalOnUtilEnabledTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java index f92cb37c..4f9696c0 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java @@ -57,4 +57,4 @@ void testConfigBean(boolean present) { assertEquals(present, isBeanPresent(context, ConditionalOnUtilEnabledTest.class)); }, ConditionalOnUtilEnabledTest.class); } -} +} \ No newline at end of file From 4a86a66e0b0c93dde01c1f08f3855796862908a3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 13:23:48 +0800 Subject: [PATCH 267/310] Update ConditionalOnLoadBalancerEnabledTest.java --- .../condition/ConditionalOnLoadBalancerEnabledTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java index 959565a4..f9d1cca6 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java @@ -57,4 +57,4 @@ void testConfigBean(boolean present) { assertEquals(present, isBeanPresent(context, ConditionalOnLoadBalancerEnabledTest.class)); }, ConditionalOnLoadBalancerEnabledTest.class); } -} +} \ No newline at end of file From 0f8b69f0f9d423081223b459c58e1f59ef8375bc Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 15:40:13 +0800 Subject: [PATCH 268/310] Update BeanUtils import to microsphere package in tests Replaced imports of isBeanPresent from com.alibaba.spring.util.BeanUtils with io.microsphere.spring.beans.BeanUtils in ConditionalOnUtilEnabledTest and ConditionalOnLoadBalancerEnabledTest to reflect package changes. --- .../cloud/commons/condition/ConditionalOnUtilEnabledTest.java | 2 +- .../condition/ConditionalOnLoadBalancerEnabledTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java index 4f9696c0..14a9895e 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java @@ -21,7 +21,7 @@ import java.util.Properties; -import static com.alibaba.spring.util.BeanUtils.isBeanPresent; +import static io.microsphere.spring.beans.BeanUtils.isBeanPresent; import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; import static java.lang.System.getProperties; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java index f9d1cca6..831dc36c 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java @@ -21,7 +21,7 @@ import java.util.Properties; -import static com.alibaba.spring.util.BeanUtils.isBeanPresent; +import static io.microsphere.spring.beans.BeanUtils.isBeanPresent; import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; import static java.lang.System.getProperties; import static org.junit.jupiter.api.Assertions.assertEquals; From 754e504bbe6cd5fd124fb4af00a4836b8fe1d3ba Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 17:18:42 +0800 Subject: [PATCH 269/310] Refactor conditional property tests to use shared base class Introduced ConditionalOnPropertyEnabledTest as a reusable abstract test class for @ConditionalOnProperty-based conditions. Updated ConditionalOnFeaturesEnabledTest, ConditionalOnUtilEnabledTest, and ConditionalOnLoadBalancerEnabledTest to extend this new base class, reducing code duplication and improving maintainability. Also set matchIfMissing=true for ConditionalOnFeaturesEnabled. --- .../ConditionalOnFeaturesEnabled.java | 2 +- .../ConditionalOnFeaturesEnabledTest.java | 34 +---- .../ConditionalOnUtilEnabledTest.java | 33 +---- .../ConditionalOnLoadBalancerEnabledTest.java | 33 +---- .../ConditionalOnPropertyEnabledTest.java | 122 ++++++++++++++++++ 5 files changed, 130 insertions(+), 94 deletions(-) create mode 100644 microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.java diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java index 1ce3f27c..fcfb14b5 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java @@ -47,7 +47,7 @@ @Retention(RUNTIME) @Target({TYPE, METHOD}) @Documented -@ConditionalOnProperty(name = FEATURES_ENABLED_PROPERTY_NAME) +@ConditionalOnProperty(name = FEATURES_ENABLED_PROPERTY_NAME, matchIfMissing = true) public @interface ConditionalOnFeaturesEnabled { /** diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java index 6e57810c..80986d9b 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabledTest.java @@ -24,36 +24,8 @@ * @since 1.0.0 */ -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; +import io.microsphere.spring.cloud.test.ConditionalOnPropertyEnabledTest; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = { - ConditionalOnFeaturesEnabledTest.FeaturesConfiguration.class -}) -@TestPropertySource( - properties = { - "spring.cloud.features.enabled=true" - } -) -class ConditionalOnFeaturesEnabledTest { - - @ConditionalOnFeaturesEnabled - static class FeaturesConfiguration { - } - - @Autowired - private ObjectProvider featuresConfigurationProvider; - - @Test - void test() { - assertNotNull(featuresConfigurationProvider.getIfAvailable()); - } +@ConditionalOnFeaturesEnabled +class ConditionalOnFeaturesEnabledTest extends ConditionalOnPropertyEnabledTest { } diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java index 14a9895e..ce62854f 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/commons/condition/ConditionalOnUtilEnabledTest.java @@ -17,14 +17,7 @@ package io.microsphere.spring.cloud.commons.condition; -import org.junit.jupiter.api.Test; - -import java.util.Properties; - -import static io.microsphere.spring.beans.BeanUtils.isBeanPresent; -import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; -import static java.lang.System.getProperties; -import static org.junit.jupiter.api.Assertions.assertEquals; +import io.microsphere.spring.cloud.test.ConditionalOnPropertyEnabledTest; /** * {@link ConditionalOnUtilEnabled} Test @@ -34,27 +27,5 @@ * @since 1.0.0 */ @ConditionalOnUtilEnabled -public class ConditionalOnUtilEnabledTest { - - @Test - void testConfigBeanPresent() { - testConfigBean(true); - } - - @Test - void testConfigBeanAbsent() { - Properties properties = getProperties(); - try { - properties.setProperty("spring.cloud.util.enabled", "false"); - testConfigBean(false); - } finally { - properties.remove("spring.cloud.util.enabled"); - } - } - - void testConfigBean(boolean present) { - testInSpringContainer(context -> { - assertEquals(present, isBeanPresent(context, ConditionalOnUtilEnabledTest.class)); - }, ConditionalOnUtilEnabledTest.class); - } +public class ConditionalOnUtilEnabledTest extends ConditionalOnPropertyEnabledTest { } \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java index 831dc36c..d251003f 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/loadbalancer/condition/ConditionalOnLoadBalancerEnabledTest.java @@ -17,14 +17,7 @@ package io.microsphere.spring.cloud.loadbalancer.condition; -import org.junit.jupiter.api.Test; - -import java.util.Properties; - -import static io.microsphere.spring.beans.BeanUtils.isBeanPresent; -import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; -import static java.lang.System.getProperties; -import static org.junit.jupiter.api.Assertions.assertEquals; +import io.microsphere.spring.cloud.test.ConditionalOnPropertyEnabledTest; /** * {@link ConditionalOnLoadBalancerEnabled @ConditionalOnLoadBalancerEnabled} Test @@ -34,27 +27,5 @@ * @since 1.0.0 */ @ConditionalOnLoadBalancerEnabled -public class ConditionalOnLoadBalancerEnabledTest { - - @Test - void testConfigBeanPresent() { - testConfigBean(true); - } - - @Test - void testConfigBeanAbsent() { - Properties properties = getProperties(); - try { - properties.setProperty("spring.cloud.loadbalancer.enabled", "false"); - testConfigBean(false); - } finally { - properties.remove("spring.cloud.loadbalancer.enabled"); - } - } - - void testConfigBean(boolean present) { - testInSpringContainer(context -> { - assertEquals(present, isBeanPresent(context, ConditionalOnLoadBalancerEnabledTest.class)); - }, ConditionalOnLoadBalancerEnabledTest.class); - } +public class ConditionalOnLoadBalancerEnabledTest extends ConditionalOnPropertyEnabledTest { } \ No newline at end of file diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.java new file mode 100644 index 00000000..42ec97f3 --- /dev/null +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.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 io.microsphere.spring.cloud.test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Conditional; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; + +import java.util.Properties; +import java.util.Set; + +import static io.microsphere.collection.SetUtils.newLinkedHashSet; +import static io.microsphere.spring.beans.BeanUtils.isBeanPresent; +import static io.microsphere.spring.core.annotation.AnnotationUtils.getAnnotationAttributes; +import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; +import static io.microsphere.util.ArrayUtils.isEmpty; +import static java.lang.String.valueOf; +import static java.lang.System.getProperties; +import static org.apache.commons.io.IOUtils.length; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.core.type.AnnotationMetadata.introspect; +import static org.springframework.util.StringUtils.hasText; + +/** + * Abstract test class for {@link ConditionalOnProperty @ConditionalOnProperty} On Enabled + * + * @author Mercy + * @see ConditionalOnProperty + * @since 1.0.0 + */ +public abstract class ConditionalOnPropertyEnabledTest { + + private AnnotationAttributes annotationAttributes; + + @BeforeEach + void setUp() { + AnnotationMetadata annotationMetadata = introspect(getClass()); + this.annotationAttributes = getAnnotationAttributes(annotationMetadata, ConditionalOnProperty.class); + } + + @Test + void testConditionalOnPropertyEnabled() { + if (matchIfMissing()) { + testBean(true); + } else { + testConditionalOnPropertyEnabled(true); + } + } + + @Test + void testConditionalOnPropertyDisabled() { + testConditionalOnPropertyEnabled(false); + } + + /** + * Whether match if missing + * + * @return {@code true} if match if missing + */ + protected boolean matchIfMissing() { + return this.annotationAttributes.getBoolean("matchIfMissing"); + } + + /** + * Get the property names of the {@link Conditional @Conditional} + * + * @return property names + */ + protected Set getPropertyNames() { + String prefix = this.annotationAttributes.getString("prefix"); + String[] names = this.annotationAttributes.getStringArray("name"); + if (isEmpty(names)) { + names = this.annotationAttributes.getStringArray("value"); + } + boolean hasPrefix = hasText(prefix); + Set propertyNames = newLinkedHashSet(length(names)); + for (String name : names) { + String propertyName = hasPrefix ? prefix + name : name; + propertyNames.add(propertyName); + } + return propertyNames; + } + + protected void testConditionalOnPropertyEnabled(boolean enabled) { + Set propertyNames = getPropertyNames(); + Properties properties = getProperties(); + try { + for (String propertyName : propertyNames) { + properties.setProperty(propertyName, valueOf(enabled)); + } + testBean(enabled); + } finally { + for (String propertyName : propertyNames) { + properties.remove(propertyName); + } + } + } + + protected void testBean(boolean present) { + testInSpringContainer(context -> { + assertEquals(present, isBeanPresent(context, getClass())); + }, getClass()); + } +} \ No newline at end of file From 29641b9ec3ae9445cb5e39fa44ae4ce5fafd9a21 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 17:23:52 +0800 Subject: [PATCH 270/310] Remove unused matchIfMissing attribute from annotation Eliminated the matchIfMissing attribute and its related @AliasFor usage from the ConditionalOnFeaturesEnabled annotation, simplifying the annotation as the property is already set via @ConditionalOnProperty. --- .../client/condition/ConditionalOnFeaturesEnabled.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java index fcfb14b5..d7058b9b 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/condition/ConditionalOnFeaturesEnabled.java @@ -21,7 +21,6 @@ import org.springframework.cloud.client.CommonsClientAutoConfiguration; import org.springframework.cloud.client.actuator.FeaturesEndpoint; import org.springframework.cloud.client.actuator.HasFeatures; -import org.springframework.core.annotation.AliasFor; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -49,13 +48,4 @@ @Documented @ConditionalOnProperty(name = FEATURES_ENABLED_PROPERTY_NAME, matchIfMissing = true) public @interface ConditionalOnFeaturesEnabled { - - /** - * Specify if the condition should match if the property is not set. Defaults to - * {@code true}. - * - * @return if the condition should match if the property is missing - */ - @AliasFor(annotation = ConditionalOnProperty.class, attribute = "matchIfMissing") - boolean matchIfMissing() default true; } From 3e9cf7cda003b3200f76bb8c88a88fa99a3ed275 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 18:57:39 +0800 Subject: [PATCH 271/310] Update ReactiveDiscoveryClientAdapter.java --- .../discovery/ReactiveDiscoveryClientAdapter.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java index 12f55e19..652ca766 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java @@ -21,9 +21,13 @@ import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.List; +import static io.microsphere.lang.function.ThrowableSupplier.execute; +import static reactor.core.scheduler.Schedulers.isInNonBlockingThread; + /** * An adapter {@link DiscoveryClient} class based on {@link ReactiveDiscoveryClient} * @@ -67,6 +71,10 @@ public int getOrder() { } static List toList(Flux flux) { - return flux.collectList().block(); + Mono> mono = flux.collectList(); + if (isInNonBlockingThread()) { + return execute(() -> mono.toFuture().get()); + } + return mono.block(); } } \ No newline at end of file From 9c96e049105ed632bb2326edfb2ab866c845ddec Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 18:57:44 +0800 Subject: [PATCH 272/310] Add tests for ReactiveDiscoveryClientAdapter.toList Introduced test cases to verify the toList utility method in ReactiveDiscoveryClientAdapter using different schedulers. Ensures correct conversion of Flux to List under various scheduling scenarios. --- .../ReactiveDiscoveryClientAdapterTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java index c6ae1384..2f65a00d 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapterTest.java @@ -26,15 +26,22 @@ import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClient; import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryProperties; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.scheduler.Scheduler; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import static io.microsphere.collection.Lists.ofList; +import static io.microsphere.spring.cloud.client.discovery.ReactiveDiscoveryClientAdapter.toList; import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtilsTest.createDefaultServiceInstance; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; +import static reactor.core.scheduler.Schedulers.immediate; +import static reactor.core.scheduler.Schedulers.newSingle; /** * {@link ReactiveDiscoveryClientAdapter} @@ -97,4 +104,21 @@ void testProbe() { void testGetOrder() { assertEquals(this.client.getOrder(), this.adapter.getOrder()); } + + @Test + void testToList() throws Exception { + assertList(immediate(), "1,2,3"); + assertList(newSingle("test"), "1,2,3"); + } + + void assertList(Scheduler scheduler, T... values) throws Exception { + Flux flux = Flux.just(values); + Disposable disposable = scheduler.schedule(() -> { + List list = toList(flux); + assertEquals(ofList(values), list); + }); + if (disposable instanceof Callable) { + ((Callable) disposable).call(); + } + } } \ No newline at end of file From 22bbdc493f423a4a6b81b9bf7d75a7b947833612 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 31 Oct 2025 19:16:05 +0800 Subject: [PATCH 273/310] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 85609907..1cd8cbdb 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ pom.xml: | **Branches** | **Purpose** | **Latest Version** | |--------------|--------------------------------------------------|--------------------| -| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.3 | -| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.3 | +| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.4 | +| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.4 | Then add the specific modules you need: From 25e1fad266c8fb91374018d562942b57fce30a5d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 7 Nov 2025 21:26:51 +0800 Subject: [PATCH 274/310] Bump project version to 0.2.5-SNAPSHOT Updated the revision property in pom.xml to prepare for the next development iteration. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ea2e813..f0424f83 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ - 0.2.4-SNAPSHOT + 0.2.5-SNAPSHOT 17 From ede3b41006eaf4c89097769e8385c834c9cb2054 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 7 Nov 2025 21:29:36 +0800 Subject: [PATCH 275/310] Update microsphere-spring-boot BOM version to 0.2.5 Bumps the microsphere-spring-boot.version property from 0.2.4 to 0.2.5 in the parent POM to use the latest BOM release. --- microsphere-spring-cloud-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 97bad6bf..84b562e5 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -20,7 +20,7 @@ - 0.2.4 + 0.2.5 2.0.1 From 4dd31d7c8fa714761c580d838cb283b3da60d4a4 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 7 Nov 2025 21:31:55 +0800 Subject: [PATCH 276/310] Remove distribution and repository config from pom.xml Deleted the and sections from pom.xml, likely to simplify project configuration or move repository management elsewhere. --- pom.xml | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/pom.xml b/pom.xml index f0424f83..011b36a2 100644 --- a/pom.xml +++ b/pom.xml @@ -63,28 +63,4 @@ microsphere-spring-cloud-openfeign - - - ossrh - https://s01.oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - snapshot - - true - - - false - - https://s01.oss.sonatype.org/content/repositories/snapshots - - - \ No newline at end of file From a036893520bfbbc7c9c25a9cc1f8e89598e3977a Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 7 Nov 2025 21:32:41 +0800 Subject: [PATCH 277/310] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 1cd8cbdb..b9c34fe0 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ [![Codecov](https://codecov.io/gh/microsphere-projects/microsphere-spring-cloud/branch/dev/graph/badge.svg)](https://app.codecov.io/gh/microsphere-projects/microsphere-spring-cloud) ![Maven](https://img.shields.io/maven-central/v/io.github.microsphere-projects/microsphere-spring-cloud.svg) ![License](https://img.shields.io/github/license/microsphere-projects/microsphere-spring-cloud.svg) -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/microsphere-projects/microsphere-spring-cloud.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-cloud "Average time to resolve an issue") -[![Percentage of issues still open](http://isitmaintained.com/badge/open/microsphere-projects/microsphere-spring-cloud.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-cloud "Percentage of issues still open") + Microsphere Spring Cloud is an extension library for Spring Cloud that enhances and optimizes its capabilities, particularly focused on providing dynamic runtime configuration changes without application restarts. It's designed to From 548048a6cfbe9f5e1328a911cefb26b3e308aa74 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 7 Nov 2025 21:34:57 +0800 Subject: [PATCH 278/310] Update latest version numbers in README Bumped the latest version numbers for branches 0.2.x and 0.1.x in the README to reflect recent releases. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b9c34fe0..b4ce298f 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ pom.xml: | **Branches** | **Purpose** | **Latest Version** | |--------------|--------------------------------------------------|--------------------| -| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.4 | -| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.4 | +| **0.2.x** | Compatible with Spring Cloud 2022.0.x - 2025.0.x | 0.2.5 | +| **0.1.x** | Compatible with Spring Cloud Hoxton - 2021.0.x | 0.1.5 | Then add the specific modules you need: From f5d51e5a10b575a0ed1060442fc294e5d3887bc3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Fri, 7 Nov 2025 21:49:49 +0800 Subject: [PATCH 279/310] Fix static import for ArrayUtils.length Replaces incorrect import of org.apache.commons.io.IOUtils.length with the correct io.microsphere.util.ArrayUtils.length in ConditionalOnPropertyEnabledTest.java. --- .../spring/cloud/test/ConditionalOnPropertyEnabledTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.java index 42ec97f3..b5b1aa7c 100644 --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.java +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/test/ConditionalOnPropertyEnabledTest.java @@ -32,9 +32,9 @@ import static io.microsphere.spring.core.annotation.AnnotationUtils.getAnnotationAttributes; import static io.microsphere.spring.test.util.SpringTestUtils.testInSpringContainer; import static io.microsphere.util.ArrayUtils.isEmpty; +import static io.microsphere.util.ArrayUtils.length; import static java.lang.String.valueOf; import static java.lang.System.getProperties; -import static org.apache.commons.io.IOUtils.length; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.core.type.AnnotationMetadata.introspect; import static org.springframework.util.StringUtils.hasText; From 91525cfc9a2cff62deeb96ab5884a8c66651793d Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 1 Dec 2025 14:13:58 +0800 Subject: [PATCH 280/310] Refactor logging to use microsphere logging API Replaced usage of slf4j Logger and LoggerFactory with io.microsphere.logging.Logger and LoggerFactory in test classes. This standardizes logging across the test suite to use the microsphere logging implementation. --- .../io/microsphere/spring/cloud/openfeign/BaseTest.java | 6 +++--- .../cloud/openfeign/ObservableFeignInvocationHandler.java | 6 +++--- .../spring/cloud/openfeign/errordecoder/AErrorDecoder.java | 6 +++--- .../cloud/openfeign/querymapencoder/AQueryMapEncoder.java | 6 +++--- .../cloud/openfeign/querymapencoder/BQueryMapEncoder.java | 6 +++--- .../microsphere/spring/cloud/openfeign/retryer/ARetry.java | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java index 0913cbe6..d9f98866 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java @@ -2,8 +2,8 @@ import io.microsphere.spring.cloud.openfeign.autoconfigure.EnableFeignAutoRefresh; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; +import static io.microsphere.logging.LoggerFactory.getLogger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.endpoint.event.RefreshEvent; @@ -41,7 +41,7 @@ @EnableFeignAutoRefresh public abstract class BaseTest { - private static final Logger log = LoggerFactory.getLogger(BaseTest.class); + private static final Logger log = getLogger(BaseTest.class); @Autowired private ApplicationEventPublisher publisher; @Autowired diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java index b23280bd..d9945196 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java @@ -3,8 +3,8 @@ import feign.InvocationHandlerFactory; import feign.ResponseHandler; import feign.Target; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; +import static io.microsphere.logging.LoggerFactory.getLogger; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -23,7 +23,7 @@ public class ObservableFeignInvocationHandler implements InvocationHandler { - private static final Logger log = LoggerFactory.getLogger(ObservableFeignInvocationHandler.class); + private static final Logger log = getLogger(ObservableFeignInvocationHandler.class); public static FeignComponentAssert componentAssert; public static Class expectComponentClass; diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java index b9a9d2a3..db06bbf4 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/errordecoder/AErrorDecoder.java @@ -2,8 +2,8 @@ import feign.Response; import feign.codec.ErrorDecoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; +import static io.microsphere.logging.LoggerFactory.getLogger; /** * @author 韩超 @@ -11,7 +11,7 @@ */ public class AErrorDecoder implements ErrorDecoder { - private static final Logger log = LoggerFactory.getLogger(AErrorDecoder.class); + private static final Logger log = getLogger(AErrorDecoder.class); @Override public Exception decode(String methodKey, Response response) { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java index 79e20f11..02e95645 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java @@ -1,8 +1,8 @@ package io.microsphere.spring.cloud.openfeign.querymapencoder; import feign.QueryMapEncoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; +import static io.microsphere.logging.LoggerFactory.getLogger; import java.util.HashMap; import java.util.Map; @@ -14,7 +14,7 @@ public class AQueryMapEncoder implements QueryMapEncoder { - private static final Logger log = LoggerFactory.getLogger(AQueryMapEncoder.class); + private static final Logger log = getLogger(AQueryMapEncoder.class); @Override public Map encode(Object object) { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java index 7d271d47..30ab38a9 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java @@ -1,8 +1,8 @@ package io.microsphere.spring.cloud.openfeign.querymapencoder; import feign.QueryMapEncoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; +import static io.microsphere.logging.LoggerFactory.getLogger; import java.util.HashMap; import java.util.Map; @@ -14,7 +14,7 @@ public class BQueryMapEncoder implements QueryMapEncoder { - private static final Logger log = LoggerFactory.getLogger(BQueryMapEncoder.class); + private static final Logger log = getLogger(BQueryMapEncoder.class); @Override public Map encode(Object object) { diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/ARetry.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/ARetry.java index b43e6436..a4856551 100644 --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/ARetry.java +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/retryer/ARetry.java @@ -2,8 +2,8 @@ import feign.RetryableException; import feign.Retryer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; +import static io.microsphere.logging.LoggerFactory.getLogger; /** * @author 韩超 @@ -11,7 +11,7 @@ */ public class ARetry implements Retryer { - private static final Logger log = LoggerFactory.getLogger(ARetry.class); + private static final Logger log = getLogger(ARetry.class); @Override public void continueOrPropagate(RetryableException e) { From 9212fb99b92c5b7feb0e02c41ed41b041ce84cb3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:40:03 +0000 Subject: [PATCH 281/310] Initial plan From 8edf288eb94b7fe3796fcf89f21cb15ee661d477 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:50:08 +0000 Subject: [PATCH 282/310] Add JavaDoc comments with Example Usage sections to discovery client classes Add JavaDoc to non-private methods missing documentation in: - ReactiveDiscoveryClientAdapter: constructor, description(), getInstances(), getServices(), probe(), getOrder() - UnionDiscoveryClient: description(), getInstances(), getServices(), getDiscoveryClients(), getOrder(), afterSingletonsInstantiated(), destroy(), setApplicationContext() - DiscoveryClientAutoConfiguration: unionDiscoveryClient() bean method - ReactiveDiscoveryClientAutoConfiguration: reactiveDiscoveryClientAdapter() bean method Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ReactiveDiscoveryClientAdapter.java | 35 +++++++++++++++ .../discovery/UnionDiscoveryClient.java | 44 +++++++++++++++++++ .../DiscoveryClientAutoConfiguration.java | 13 ++++++ ...ctiveDiscoveryClientAutoConfiguration.java | 14 ++++++ 4 files changed, 106 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java index 652ca766..73ba2fe2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java @@ -39,32 +39,67 @@ public class ReactiveDiscoveryClientAdapter implements DiscoveryClient { private final ReactiveDiscoveryClient reactiveDiscoveryClient; + /** + * Create a new {@link ReactiveDiscoveryClientAdapter} that wraps the given + * {@link ReactiveDiscoveryClient} as a blocking {@link DiscoveryClient}. + * + *

Example Usage: + *

{@code
+     * ReactiveDiscoveryClient reactiveClient = new SimpleReactiveDiscoveryClient(properties);
+     * DiscoveryClient adapter = new ReactiveDiscoveryClientAdapter(reactiveClient);
+     * List services = adapter.getServices();
+     * }
+ * + * @param reactiveDiscoveryClient the {@link ReactiveDiscoveryClient} to adapt, must not be {@code null} + */ public ReactiveDiscoveryClientAdapter(ReactiveDiscoveryClient reactiveDiscoveryClient) { this.reactiveDiscoveryClient = reactiveDiscoveryClient; } + /** + * {@inheritDoc} + *

Delegates to the underlying {@link ReactiveDiscoveryClient#description()}. + */ @Override public String description() { return this.reactiveDiscoveryClient.description(); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#getInstances(String)} and collects the + * reactive {@link Flux} result into a blocking {@link List}. + */ @Override public List getInstances(String serviceId) { Flux flux = this.reactiveDiscoveryClient.getInstances(serviceId); return toList(flux); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#getServices()} and collects the + * reactive {@link Flux} result into a blocking {@link List}. + */ @Override public List getServices() { Flux flux = this.reactiveDiscoveryClient.getServices(); return toList(flux); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#probe()}. + */ @Override public void probe() { this.reactiveDiscoveryClient.probe(); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#getOrder()}. + */ @Override public int getOrder() { return this.reactiveDiscoveryClient.getOrder(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java index 944f0cdd..8b19619c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java @@ -48,11 +48,20 @@ public final class UnionDiscoveryClient implements DiscoveryClient, ApplicationC private List discoveryClients; + /** + * {@inheritDoc} + * + * @return the description "Union Discovery Client" + */ @Override public String description() { return "Union Discovery Client"; } + /** + * {@inheritDoc} + *

Aggregates service instances from all underlying {@link DiscoveryClient DiscoveryClients}. + */ @Override public List getInstances(String serviceId) { List serviceInstances = new LinkedList<>(); @@ -66,6 +75,10 @@ public List getInstances(String serviceId) { return serviceInstances; } + /** + * {@inheritDoc} + *

Returns a deduplicated union of service names from all underlying {@link DiscoveryClient DiscoveryClients}. + */ @Override public List getServices() { LinkedHashSet services = new LinkedHashSet<>(); @@ -79,6 +92,20 @@ public List getServices() { return new ArrayList<>(services); } + /** + * Returns the sorted list of underlying {@link DiscoveryClient DiscoveryClients}, excluding + * {@link CompositeDiscoveryClient} and this instance itself. The list is lazily initialized + * from the {@link ApplicationContext} on first access and cached for subsequent calls. + * + *

Example Usage: + *

{@code
+     * UnionDiscoveryClient unionClient = applicationContext.getBean(UnionDiscoveryClient.class);
+     * List clients = unionClient.getDiscoveryClients();
+     * clients.forEach(c -> System.out.println(c.description()));
+     * }
+ * + * @return an unmodifiable list of {@link DiscoveryClient} instances + */ public List getDiscoveryClients() { List discoveryClients = this.discoveryClients; if (discoveryClients != null) { @@ -98,21 +125,38 @@ public List getDiscoveryClients() { return discoveryClients; } + /** + * {@inheritDoc} + * + * @return {@link #HIGHEST_PRECEDENCE} to ensure this client takes priority + */ @Override public int getOrder() { return HIGHEST_PRECEDENCE; } + /** + * {@inheritDoc} + *

Eagerly initializes the list of {@link DiscoveryClient DiscoveryClients} after all singletons are instantiated. + */ @Override public void afterSingletonsInstantiated() { this.discoveryClients = getDiscoveryClients(); } + /** + * {@inheritDoc} + *

Clears the cached list of {@link DiscoveryClient DiscoveryClients} on bean destruction. + */ @Override public void destroy() throws Exception { this.discoveryClients.clear(); } + /** + * {@inheritDoc} + *

Stores the {@link ApplicationContext} used to look up {@link DiscoveryClient} beans. + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java index 28f9b1e7..7fc2c6eb 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java @@ -80,6 +80,19 @@ public class DiscoveryClientAutoConfiguration { @ConditionalOnProperty(name = DISCOVERY_CLIENT_MODE_PROPERTY_NAME, havingValue = UNION_DISCOVERY_CLIENT_MODE) public static class UnionConfiguration { + /** + * Creates a {@link UnionDiscoveryClient} bean that aggregates all {@link DiscoveryClient} + * instances in the {@link org.springframework.context.ApplicationContext}. + * + *

Example Usage: + *

{@code
+         * // Activated when microsphere.spring.cloud.client.discovery.mode=union
+         * UnionDiscoveryClient client = applicationContext.getBean(UnionDiscoveryClient.class);
+         * List services = client.getServices();
+         * }
+ * + * @return a new {@link UnionDiscoveryClient} instance + */ @Bean public UnionDiscoveryClient unionDiscoveryClient() { return new UnionDiscoveryClient(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java index 26aa7b18..8cb3a906 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java @@ -61,6 +61,20 @@ public class ReactiveDiscoveryClientAutoConfiguration { @ConditionalOnBlockingDiscoveryEnabled public static class BlockingConfiguration { + /** + * Creates a {@link ReactiveDiscoveryClientAdapter} bean that adapts a + * {@link ReactiveDiscoveryClient} to the blocking {@link org.springframework.cloud.client.discovery.DiscoveryClient} interface. + * + *

Example Usage: + *

{@code
+         * // Auto-configured when both reactive and blocking discovery are enabled
+         * DiscoveryClient client = applicationContext.getBean(ReactiveDiscoveryClientAdapter.class);
+         * List services = client.getServices();
+         * }
+ * + * @param reactiveDiscoveryClient the {@link ReactiveDiscoveryClient} to adapt + * @return a new {@link ReactiveDiscoveryClientAdapter} instance + */ @Bean @ConditionalOnBean(ReactiveDiscoveryClient.class) public ReactiveDiscoveryClientAdapter reactiveDiscoveryClientAdapter(ReactiveDiscoveryClient reactiveDiscoveryClient) { From 59def04f14a4f107e2b80d28d07f98fa9f70a859 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:55:31 +0000 Subject: [PATCH 283/310] Add JavaDoc comments with Example Usage sections to non-private methods Add comprehensive JavaDoc documentation to non-private methods that were missing it in the service registry package: - InMemoryServiceRegistry: all @Override methods and getMetadata() - MultipleAutoServiceRegistration: constructor and all @Override methods - MultipleRegistration: constructor, all @Override methods, getDefaultRegistration(), special() - MultipleServiceRegistry: constructor, all @Override methods, getRegistrationClass() - RegistrationMetaData: constructor and all Map @Override methods - SimpleAutoServiceRegistration: constructor and all @Override methods - SimpleServiceRegistry: all constructors, @Override methods, and package-private getInstances() Each JavaDoc includes: - Brief description with {@link} cross-references - Example Usage section with
{@code ...}
blocks - @param, @return tags where applicable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../registry/InMemoryServiceRegistry.java | 78 +++++++++ .../MultipleAutoServiceRegistration.java | 65 +++++++ .../registry/MultipleRegistration.java | 120 +++++++++++++ .../registry/MultipleServiceRegistry.java | 96 ++++++++++ .../registry/RegistrationMetaData.java | 164 ++++++++++++++++++ .../SimpleAutoServiceRegistration.java | 64 +++++++ .../registry/SimpleServiceRegistry.java | 126 ++++++++++++++ 7 files changed, 713 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java index 26da337e..7d1840c3 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java @@ -35,23 +35,73 @@ public class InMemoryServiceRegistry implements ServiceRegistry { private final ConcurrentMap storage = new ConcurrentHashMap<>(1); + /** + * Registers the given {@link Registration} instance in the in-memory storage, + * keyed by its instance ID. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * Registration registration = createRegistration();
+     * registry.register(registration);
+     * }
+ * + * @param registration the {@link Registration} to store + */ @Override public void register(Registration registration) { String id = registration.getInstanceId(); storage.put(id, registration); } + /** + * Removes the given {@link Registration} instance from the in-memory storage. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * Registration registration = createRegistration();
+     * registry.register(registration);
+     * registry.deregister(registration);
+     * }
+ * + * @param registration the {@link Registration} to remove + */ @Override public void deregister(Registration registration) { String id = registration.getInstanceId(); storage.remove(id, registration); } + /** + * Closes this registry by clearing all stored {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * registry.close();
+     * }
+ */ @Override public void close() { storage.clear(); } + /** + * Sets the status of the given {@link Registration} by storing it in + * the registration's metadata under the {@code _status_} key. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * }
+ * + * @param registration the {@link Registration} whose status is to be set + * @param status the status value to set + */ @Override public void setStatus(Registration registration, String status) { Map metadata = getMetadata(registration); @@ -60,6 +110,20 @@ public void setStatus(Registration registration, String status) { } } + /** + * Retrieves the status of the given {@link Registration} from its metadata. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * Object status = registry.getStatus(registration); // "UP"
+     * }
+ * + * @param registration the {@link Registration} whose status is to be retrieved + * @return the status value, or {@code null} if not set or registration not found + */ @Override public Object getStatus(Registration registration) { Map metadata = getMetadata(registration); @@ -69,6 +133,20 @@ public Object getStatus(Registration registration) { return null; } + /** + * Retrieves the metadata {@link Map} for the given {@link Registration} + * from the in-memory storage. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * Map metadata = registry.getMetadata(registration);
+     * }
+ * + * @param registration the {@link Registration} whose metadata is to be retrieved + * @return the metadata map, or {@code null} if the registration is not found + */ protected Map getMetadata(Registration registration) { String id = registration.getInstanceId(); Registration instance = storage.get(id); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java index 70b06231..227305f5 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java @@ -20,6 +20,24 @@ public class MultipleAutoServiceRegistration extends AbstractAutoServiceRegistra private final MultipleRegistration multipleRegistration; + /** + * Constructs a new {@link MultipleAutoServiceRegistration} with the specified + * {@link MultipleRegistration}, {@link ServiceRegistry}, and + * {@link AutoServiceRegistrationProperties}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration registration = new MultipleRegistration(registrations);
+     * ServiceRegistry serviceRegistry = new InMemoryServiceRegistry();
+     * AutoServiceRegistrationProperties properties = new AutoServiceRegistrationProperties();
+     * MultipleAutoServiceRegistration autoReg =
+     *     new MultipleAutoServiceRegistration(registration, serviceRegistry, properties);
+     * }
+ * + * @param multipleRegistration the {@link MultipleRegistration} to manage + * @param serviceRegistry the {@link ServiceRegistry} to delegate to + * @param properties the {@link AutoServiceRegistrationProperties} for configuration + */ public MultipleAutoServiceRegistration(MultipleRegistration multipleRegistration, ServiceRegistry serviceRegistry, AutoServiceRegistrationProperties properties) { @@ -28,21 +46,68 @@ public MultipleAutoServiceRegistration(MultipleRegistration multipleRegistration this.multipleRegistration = multipleRegistration; } + /** + * Returns the configuration object for this auto service registration. + * This implementation always returns {@code null}. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * Object config = autoReg.getConfiguration(); // null
+     * }
+ * + * @return {@code null} + */ @Override protected Object getConfiguration() { return null; } + /** + * Determines whether this auto service registration is enabled based on the + * {@link AutoServiceRegistrationProperties}. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * boolean enabled = autoReg.isEnabled();
+     * }
+ * + * @return {@code true} if auto service registration is enabled + */ @Override protected boolean isEnabled() { return this.autoServiceRegistrationProperties.isEnabled(); } + /** + * Returns the {@link MultipleRegistration} managed by this auto service registration. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * MultipleRegistration registration = autoReg.getRegistration();
+     * }
+ * + * @return the {@link MultipleRegistration} instance + */ @Override protected MultipleRegistration getRegistration() { return this.multipleRegistration; } + /** + * Returns the management {@link MultipleRegistration}, which is the same as + * the primary registration in this implementation. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * MultipleRegistration mgmtRegistration = autoReg.getManagementRegistration();
+     * }
+ * + * @return the {@link MultipleRegistration} instance used for management + */ @Override protected MultipleRegistration getManagementRegistration() { return this.multipleRegistration; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java index 23783914..aba1ff63 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java @@ -29,6 +29,21 @@ public class MultipleRegistration implements Registration { private final RegistrationMetaData metaData; + /** + * Constructs a new {@link MultipleRegistration} from the given collection of + * {@link Registration} instances. The last registration in the collection becomes + * the default registration. + * + *

Example Usage: + *

{@code
+     * DefaultRegistration registration = new DefaultRegistration();
+     * registration.setServiceId("test-service");
+     * MultipleRegistration multipleRegistration =
+     *     new MultipleRegistration(List.of(registration));
+     * }
+ * + * @param registrations the collection of {@link Registration} instances, must not be empty + */ public MultipleRegistration(Collection registrations) { assertNotEmpty(registrations, () -> "registrations cannot be empty"); //init map @@ -41,45 +56,150 @@ public MultipleRegistration(Collection registrations) { this.metaData = new RegistrationMetaData(registrations); } + /** + * Returns the instance ID from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * String instanceId = multipleRegistration.getInstanceId();
+     * }
+ * + * @return the instance ID of the default registration + */ @Override public String getInstanceId() { return getDefaultRegistration().getInstanceId(); } + /** + * Returns the service ID from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * String serviceId = multipleRegistration.getServiceId();
+     * }
+ * + * @return the service ID of the default registration + */ @Override public String getServiceId() { return getDefaultRegistration().getServiceId(); } + /** + * Returns the host from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * String host = multipleRegistration.getHost();
+     * }
+ * + * @return the host of the default registration + */ @Override public String getHost() { return getDefaultRegistration().getHost(); } + /** + * Returns the port from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * int port = multipleRegistration.getPort();
+     * }
+ * + * @return the port of the default registration + */ @Override public int getPort() { return getDefaultRegistration().getPort(); } + /** + * Returns whether the default {@link Registration} is secure. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * boolean secure = multipleRegistration.isSecure();
+     * }
+ * + * @return {@code true} if the default registration is secure + */ @Override public boolean isSecure() { return getDefaultRegistration().isSecure(); } + /** + * Returns the {@link URI} from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * URI uri = multipleRegistration.getUri();
+     * }
+ * + * @return the URI of the default registration + */ @Override public URI getUri() { return getDefaultRegistration().getUri(); } + /** + * Returns the aggregated {@link RegistrationMetaData} that synchronizes metadata + * across all underlying {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * Map metadata = multipleRegistration.getMetadata();
+     * }
+ * + * @return the aggregated metadata map + */ @Override public Map getMetadata() { return metaData; } + /** + * Returns the default {@link Registration} instance, which is the last registration + * provided during construction. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * Registration defaultReg = multipleRegistration.getDefaultRegistration();
+     * }
+ * + * @return the default {@link Registration} + */ public Registration getDefaultRegistration() { return defaultRegistration; } + /** + * Retrieves a specific {@link Registration} by its class type. If the specified + * class is {@link Registration} itself, returns this {@link MultipleRegistration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * DefaultRegistration specific = multipleRegistration.special(DefaultRegistration.class);
+     * Registration self = multipleRegistration.special(Registration.class);
+     * }
+ * + * @param specialClass the specific {@link Registration} subclass to look up + * @param the type of the registration + * @return the matching registration, or {@code null} if not found + */ public T special(Class specialClass) { if (Registration.class.equals(specialClass)) return (T) this; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java index 4b5aa292..d87c3118 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java @@ -37,6 +37,21 @@ public class MultipleServiceRegistry implements ServiceRegistryExample Usage: + *
{@code
+     * ServiceRegistry simpleRegistry = new InMemoryServiceRegistry();
+     * MultipleServiceRegistry registry =
+     *     new MultipleServiceRegistry(Map.of("default", simpleRegistry));
+     * }
+ * + * @param registriesMap the map of Spring bean names to {@link ServiceRegistry} instances, + * must not be empty + */ public MultipleServiceRegistry(Map registriesMap) { assertNotEmpty(registriesMap, () -> "registrations cannot be empty"); @@ -53,21 +68,71 @@ public MultipleServiceRegistry(Map registriesMap) { } } + /** + * Registers the given {@link MultipleRegistration} by delegating to each underlying + * {@link ServiceRegistry} with the corresponding specific {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * MultipleRegistration registration = new MultipleRegistration(registrations);
+     * registry.register(registration);
+     * }
+ * + * @param registration the {@link MultipleRegistration} to register + */ @Override public void register(MultipleRegistration registration) { iterate(registration, (reg, registry) -> registry.register(reg)); } + /** + * Deregisters the given {@link MultipleRegistration} by delegating to each underlying + * {@link ServiceRegistry} with the corresponding specific {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * MultipleRegistration registration = new MultipleRegistration(registrations);
+     * registry.register(registration);
+     * registry.deregister(registration);
+     * }
+ * + * @param registration the {@link MultipleRegistration} to deregister + */ @Override public void deregister(MultipleRegistration registration) { iterate(registration, (reg, registry) -> registry.deregister(reg)); } + /** + * Closes all underlying {@link ServiceRegistry} instances. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * registry.close();
+     * }
+ */ @Override public void close() { iterate(ServiceRegistry::close); } + /** + * Sets the status of the given {@link MultipleRegistration} by delegating to each + * underlying {@link ServiceRegistry} with the corresponding specific {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * }
+ * + * @param registration the {@link MultipleRegistration} whose status is to be set + * @param status the status value to set + */ @Override public void setStatus(MultipleRegistration registration, String status) { iterate(registration, (reg, registry) -> registry.setStatus(reg, status)); @@ -87,6 +152,22 @@ private void iterate(Consumer action) { registriesMap.values().forEach(action); } + /** + * Retrieves the status of the given {@link MultipleRegistration} from the default + * {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * Object status = registry.getStatus(registration); // "UP"
+     * }
+ * + * @param registration the {@link MultipleRegistration} whose status is to be retrieved + * @param the type of the status value + * @return the status from the default service registry + */ @Override public T getStatus(MultipleRegistration registration) { Class registrationClass = beanNameToRegistrationTypesMap.get(defaultRegistrationBeanName); @@ -94,6 +175,21 @@ public T getStatus(MultipleRegistration registration) { return (T) defaultServiceRegistry.getStatus(targetRegistration); } + /** + * Resolves the {@link Registration} class for the given {@link ServiceRegistry} class + * using generic type resolution. Falls back to {@code SpringFactoriesLoader} when the + * generic type resolves to {@link Registration} itself. + * + *

Example Usage: + *

{@code
+     * Class regClass =
+     *     MultipleServiceRegistry.getRegistrationClass(NacosServiceRegistry.class);
+     * // returns NacosRegistration.class
+     * }
+ * + * @param serviceRegistryClass the {@link ServiceRegistry} implementation class + * @return the resolved {@link Registration} subclass + */ static Class getRegistrationClass(Class serviceRegistryClass) { Class registrationClass = forClass(serviceRegistryClass) .as(ServiceRegistry.class) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java index 6cab1bc0..638acce7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java @@ -40,6 +40,20 @@ public final class RegistrationMetaData implements Map { private final Object lock = new Object(); + /** + * Constructs a new {@link RegistrationMetaData} that aggregates metadata from the given + * collection of {@link Registration} instances. Metadata changes are synchronized across + * all registrations. + * + *

Example Usage: + *

{@code
+     * DefaultRegistration registration = new DefaultRegistration();
+     * registration.getMetadata().put("key1", "value1");
+     * RegistrationMetaData metaData = new RegistrationMetaData(List.of(registration));
+     * }
+ * + * @param registrations the collection of {@link Registration} instances, must not be empty + */ public RegistrationMetaData(Collection registrations) { assertNotEmpty(registrations, () -> "registrations cannot be empty"); this.registrations = registrations; @@ -59,31 +73,107 @@ public RegistrationMetaData(Collection registrations) { } } + /** + * Returns the number of metadata entries. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * int count = metaData.size(); // e.g. 3
+     * }
+ * + * @return the number of key-value mappings in this metadata + */ @Override public int size() { return applicationMetaData.size(); } + /** + * Returns whether this metadata map is empty. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * boolean empty = metaData.isEmpty(); // false if registrations have metadata
+     * }
+ * + * @return {@code true} if this metadata contains no entries + */ @Override public boolean isEmpty() { return this.applicationMetaData.isEmpty(); } + /** + * Returns whether this metadata contains the specified key. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * boolean hasKey = metaData.containsKey("key1"); // true
+     * boolean missing = metaData.containsKey("unknown"); // false
+     * }
+ * + * @param key the key to check for + * @return {@code true} if this metadata contains the specified key + */ @Override public boolean containsKey(Object key) { return this.applicationMetaData.containsKey(key); } + /** + * Returns whether this metadata contains the specified value. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * boolean hasValue = metaData.containsValue("value1"); // true
+     * boolean missing = metaData.containsValue("unknown"); // false
+     * }
+ * + * @param value the value to check for + * @return {@code true} if this metadata contains the specified value + */ @Override public boolean containsValue(Object value) { return this.applicationMetaData.containsValue(value); } + /** + * Returns the metadata value associated with the specified key. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * String value = metaData.get("key1"); // "value1"
+     * String missing = metaData.get("unknown"); // null
+     * }
+ * + * @param key the key whose associated value is to be returned + * @return the value associated with the key, or {@code null} if not found + */ @Override public String get(Object key) { return this.applicationMetaData.get(key); } + /** + * Puts a metadata entry and synchronizes it across all underlying {@link Registration} + * instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.put("key4", "value4");
+     * String value = metaData.get("key4"); // "value4"
+     * }
+ * + * @param key the metadata key + * @param value the metadata value + * @return the previous value associated with the key, or {@code null} + */ @Override public String put(String key, String value) { synchronized (lock) { @@ -94,6 +184,20 @@ public String put(String key, String value) { return this.applicationMetaData.put(key, value); } + /** + * Removes the metadata entry for the specified key and synchronizes the removal + * across all underlying {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.remove("key1");
+     * String value = metaData.get("key1"); // null
+     * }
+ * + * @param key the key whose mapping is to be removed + * @return the previous value associated with the key, or {@code null} + */ @Override public String remove(Object key) { synchronized (lock) { @@ -104,6 +208,18 @@ public String remove(Object key) { return this.applicationMetaData.remove(key); } + /** + * Copies all entries from the specified map into this metadata and synchronizes + * them across all underlying {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.putAll(Map.of("key4", "value4", "key5", "value5"));
+     * }
+ * + * @param m the map of entries to add + */ @Override public void putAll(Map m) { synchronized (lock) { @@ -114,6 +230,17 @@ public void putAll(Map m) { this.applicationMetaData.putAll(m); } + /** + * Clears all metadata entries and synchronizes the clearing across all underlying + * {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.clear();
+     * int size = metaData.size(); // 0
+     * }
+ */ @Override public void clear() { synchronized (lock) { @@ -122,16 +249,53 @@ public void clear() { this.applicationMetaData.clear(); } + /** + * Returns an unmodifiable {@link Set} view of the metadata keys. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * Set keys = metaData.keySet();
+     * boolean hasKey = keys.contains("key1"); // true
+     * }
+ * + * @return an unmodifiable set of metadata keys + */ @Override public Set keySet() { return Collections.unmodifiableSet(this.applicationMetaData.keySet()); } + /** + * Returns an unmodifiable {@link Collection} view of the metadata values. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * Collection values = metaData.values();
+     * boolean hasValue = values.contains("value1"); // true
+     * }
+ * + * @return an unmodifiable collection of metadata values + */ @Override public Collection values() { return Collections.unmodifiableCollection(this.applicationMetaData.values()); } + /** + * Returns a modifiable {@link Set} view of the metadata entries. Unlike + * {@link #keySet()} and {@link #values()}, the returned set is not wrapped + * in an unmodifiable view. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * Set> entries = metaData.entrySet();
+     * }
+ * + * @return a set of metadata entries + */ @Override public Set> entrySet() { return this.applicationMetaData.entrySet(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java index c0409a1b..26c3c4d2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java @@ -34,6 +34,24 @@ public class SimpleAutoServiceRegistration extends AbstractAutoServiceRegistrati private final Registration registration; + /** + * Constructs a new {@link SimpleAutoServiceRegistration} with the specified + * {@link ServiceRegistry}, {@link AutoServiceRegistrationProperties}, and + * {@link Registration}. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry serviceRegistry = new InMemoryServiceRegistry();
+     * AutoServiceRegistrationProperties properties = new AutoServiceRegistrationProperties();
+     * Registration registration = createRegistration();
+     * SimpleAutoServiceRegistration autoReg =
+     *     new SimpleAutoServiceRegistration(serviceRegistry, properties, registration);
+     * }
+ * + * @param serviceRegistry the {@link ServiceRegistry} to delegate to + * @param properties the {@link AutoServiceRegistrationProperties} for configuration + * @param registration the {@link Registration} to manage + */ public SimpleAutoServiceRegistration(ServiceRegistry serviceRegistry, AutoServiceRegistrationProperties properties, Registration registration) { super(serviceRegistry, properties); @@ -41,21 +59,67 @@ public SimpleAutoServiceRegistration(ServiceRegistry serviceRegist this.registration = registration; } + /** + * Returns the {@link AutoServiceRegistrationProperties} as the configuration object. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * Object config = autoReg.getConfiguration();
+     * }
+ * + * @return the {@link AutoServiceRegistrationProperties} instance + */ @Override protected Object getConfiguration() { return properties; } + /** + * Determines whether this auto service registration is enabled based on the + * {@link AutoServiceRegistrationProperties}. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * boolean enabled = autoReg.isEnabled();
+     * }
+ * + * @return {@code true} if auto service registration is enabled + */ @Override protected boolean isEnabled() { return properties.isEnabled(); } + /** + * Returns the {@link Registration} managed by this auto service registration. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * Registration registration = autoReg.getRegistration();
+     * }
+ * + * @return the {@link Registration} instance + */ @Override protected Registration getRegistration() { return registration; } + /** + * Returns the management {@link Registration}, which is the same as the primary + * registration in this implementation. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * Registration mgmtRegistration = autoReg.getManagementRegistration();
+     * }
+ * + * @return the {@link Registration} instance used for management + */ @Override protected Registration getManagementRegistration() { return registration; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java index 6d90e12b..386bd33c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java @@ -48,48 +48,174 @@ public class SimpleServiceRegistry implements ServiceRegistry> instancesMap; + /** + * Constructs a new {@link SimpleServiceRegistry} using the given + * {@link SimpleDiscoveryProperties} to obtain the instances map. + * + *

Example Usage: + *

{@code
+     * SimpleDiscoveryProperties properties = new SimpleDiscoveryProperties();
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * }
+ * + * @param properties the {@link SimpleDiscoveryProperties} to use + */ public SimpleServiceRegistry(SimpleDiscoveryProperties properties) { this(getInstancesMap(properties)); } + /** + * Constructs a new {@link SimpleServiceRegistry} using the given + * {@link SimpleReactiveDiscoveryProperties} to obtain the instances map. + * + *

Example Usage: + *

{@code
+     * SimpleReactiveDiscoveryProperties reactiveProperties = ...;
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(reactiveProperties);
+     * }
+ * + * @param properties the {@link SimpleReactiveDiscoveryProperties} to use + */ public SimpleServiceRegistry(SimpleReactiveDiscoveryProperties properties) { this(getInstancesMap(properties)); } + /** + * Constructs a new {@link SimpleServiceRegistry} with the given instances map. + * + *

Example Usage: + *

{@code
+     * Map> instancesMap = new HashMap<>();
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(instancesMap);
+     * }
+ * + * @param instancesMap the map of service IDs to {@link DefaultServiceInstance} lists + */ public SimpleServiceRegistry(Map> instancesMap) { this.instancesMap = instancesMap; } + /** + * Registers the given {@link DefaultRegistration} by adding it to the instances list + * for its service ID. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * DefaultRegistration registration = new DefaultRegistration();
+     * registration.setServiceId("test-service");
+     * registry.register(registration);
+     * }
+ * + * @param registration the {@link DefaultRegistration} to register + */ @Override public void register(DefaultRegistration registration) { List instances = getInstances(registration); instances.add(registration); } + /** + * Deregisters the given {@link DefaultRegistration} by removing it from the instances + * list for its service ID. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * registry.deregister(registration);
+     * }
+ * + * @param registration the {@link DefaultRegistration} to deregister + */ @Override public void deregister(DefaultRegistration registration) { List instances = getInstances(registration); instances.remove(registration); } + /** + * Closes this registry. This implementation is a no-op. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.close();
+     * }
+ */ @Override public void close() { } + /** + * Sets the status of the given {@link DefaultRegistration} by storing it in the + * registration's metadata under the {@link #STATUS_KEY} key. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * }
+ * + * @param registration the {@link DefaultRegistration} whose status is to be set + * @param status the status value to set + */ @Override public void setStatus(DefaultRegistration registration, String status) { setMetadata(registration, STATUS_KEY, status); } + /** + * Retrieves the status of the given {@link DefaultRegistration} from its metadata. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * String status = registry.getStatus(registration); // "UP"
+     * }
+ * + * @param registration the {@link DefaultRegistration} whose status is to be retrieved + * @return the status value, or {@code null} if not set + */ @Override public String getStatus(DefaultRegistration registration) { return getMetadata(registration, STATUS_KEY); } + /** + * Returns the list of {@link DefaultServiceInstance} instances for the given + * {@link DefaultRegistration}'s service ID. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * List instances = registry.getInstances(registration);
+     * }
+ * + * @param registration the {@link DefaultRegistration} to look up instances for + * @return the list of instances for the registration's service ID + */ List getInstances(DefaultRegistration registration) { return getInstances(registration.getServiceId()); } + /** + * Returns the list of {@link DefaultServiceInstance} instances for the given service ID, + * creating an empty list if none exists. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * List instances = registry.getInstances("test-service");
+     * }
+ * + * @param serviceId the service ID to look up + * @return the list of instances for the service ID + */ List getInstances(String serviceId) { return this.instancesMap.computeIfAbsent(serviceId, k -> new ArrayList<>()); } From ab3719d3f22f742f5a2fc30eea1c53fe839d9081 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:59:59 +0000 Subject: [PATCH 284/310] Add JavaDoc with Example Usage sections to non-private methods Add comprehensive JavaDoc comments with Example Usage sections using
{@code ...}
blocks to all non-private methods that were missing documentation across 10 service registry related files. Methods documented include @Bean factory methods, AOP advices, lifecycle callbacks, actuator endpoint operations, and protected template methods. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...RegistrationEndpointAutoConfiguration.java | 26 ++++++++ .../EventPublishingRegistrationAspect.java | 65 +++++++++++++++++++ .../ServiceRegistryAutoConfiguration.java | 46 +++++++++++++ ...oServiceRegistrationAutoConfiguration.java | 29 +++++++++ ...bFluxServiceRegistryAutoConfiguration.java | 26 ++++++++ ...ebMvcServiceRegistryAutoConfiguration.java | 26 ++++++++ .../WebServiceRegistryAutoConfiguration.java | 13 ++++ .../AbstractServiceRegistrationEndpoint.java | 58 +++++++++++++++++ .../ServiceDeregistrationEndpoint.java | 13 ++++ .../endpoint/ServiceRegistrationEndpoint.java | 28 ++++++++ 10 files changed, 330 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java index 3b336488..531fc200 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java @@ -49,6 +49,19 @@ }) public class ServiceRegistrationEndpointAutoConfiguration { + /** + * Creates a {@link ServiceRegistrationEndpoint} bean for managing service registration via actuator. + * + *

Example Usage: + *

{@code
+     * // The endpoint is auto-configured and accessible at /actuator/serviceRegistration
+     * @Autowired
+     * ServiceRegistrationEndpoint endpoint;
+     * Map metadata = endpoint.metadata();
+     * }
+ * + * @return a new {@link ServiceRegistrationEndpoint} instance + */ @Bean @ConditionalOnMissingBean @ConditionalOnAvailableEndpoint @@ -56,6 +69,19 @@ public ServiceRegistrationEndpoint serviceRegistrationEndpoint() { return new ServiceRegistrationEndpoint(); } + /** + * Creates a {@link ServiceDeregistrationEndpoint} bean for managing service deregistration via actuator. + * + *

Example Usage: + *

{@code
+     * // The endpoint is auto-configured and accessible at /actuator/serviceDeregistration
+     * @Autowired
+     * ServiceDeregistrationEndpoint endpoint;
+     * boolean wasRunning = endpoint.stop();
+     * }
+ * + * @return a new {@link ServiceDeregistrationEndpoint} instance + */ @Bean @ConditionalOnMissingBean @ConditionalOnAvailableEndpoint diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java index de785f4b..0c99526a 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java @@ -59,6 +59,20 @@ public class EventPublishingRegistrationAspect implements ApplicationContextAwar private ObjectProvider registrationCustomizers; + /** + * AOP advice executed before {@link ServiceRegistry#register(Registration)}, publishing a + * {@link RegistrationPreRegisteredEvent} and applying {@link RegistrationCustomizer customizations}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically when ServiceRegistry.register() is called:
+     * serviceRegistry.register(registration);
+     * // A RegistrationPreRegisteredEvent is published before actual registration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} being registered + */ @Before(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeRegister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -70,6 +84,20 @@ public void beforeRegister(ServiceRegistry registry, Registration registration) }); } + /** + * AOP advice executed before {@link ServiceRegistry#deregister(Registration)}, publishing a + * {@link RegistrationPreDeregisteredEvent}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically when ServiceRegistry.deregister() is called:
+     * serviceRegistry.deregister(registration);
+     * // A RegistrationPreDeregisteredEvent is published before actual deregistration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} being deregistered + */ @Before(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeDeregister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -78,6 +106,20 @@ public void beforeDeregister(ServiceRegistry registry, Registration registration context.publishEvent(new RegistrationPreDeregisteredEvent(registry, registration)); } + /** + * AOP advice executed after {@link ServiceRegistry#register(Registration)}, publishing a + * {@link RegistrationRegisteredEvent}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically after ServiceRegistry.register() completes:
+     * serviceRegistry.register(registration);
+     * // A RegistrationRegisteredEvent is published after successful registration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} that was registered + */ @After(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void afterRegister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -86,6 +128,20 @@ public void afterRegister(ServiceRegistry registry, Registration registration) { context.publishEvent(new RegistrationRegisteredEvent(registry, registration)); } + /** + * AOP advice executed after {@link ServiceRegistry#deregister(Registration)}, publishing a + * {@link RegistrationDeregisteredEvent}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically after ServiceRegistry.deregister() completes:
+     * serviceRegistry.deregister(registration);
+     * // A RegistrationDeregisteredEvent is published after successful deregistration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} that was deregistered + */ @After(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void afterDeregister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -98,6 +154,15 @@ boolean isIgnored(ServiceRegistry registry) { return MultipleServiceRegistry.class.isAssignableFrom(registry.getClass()); } + /** + * {@inheritDoc} + * + *

Example Usage: + *

{@code
+     * EventPublishingRegistrationAspect aspect = new EventPublishingRegistrationAspect();
+     * aspect.setApplicationContext(applicationContext);
+     * }
+ */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java index ba838d6d..9eb53ecc 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java @@ -55,6 +55,20 @@ public class ServiceRegistryAutoConfiguration { @ConditionalOnMultipleRegistrationEnabled static class MultipleConfiguration { + /** + * Creates a primary {@link MultipleRegistration} bean that aggregates all available + * {@link Registration} instances. + * + *

Example Usage: + *

{@code
+         * @Autowired
+         * MultipleRegistration multipleRegistration;
+         * // Access individual registrations from the composite
+         * }
+ * + * @param registrations the collection of {@link Registration} instances + * @return a new {@link MultipleRegistration} aggregating the provided registrations + */ @Primary @Bean @ConditionalOnMissingBean @@ -62,6 +76,21 @@ public MultipleRegistration multipleRegistration(Collection regist return new MultipleRegistration(registrations); } + /** + * Creates a primary {@link MultipleServiceRegistry} bean that delegates to all available + * {@link ServiceRegistry} instances. + * + *

Example Usage: + *

{@code
+         * @Autowired
+         * MultipleServiceRegistry multipleServiceRegistry;
+         * // Register with all service registries at once
+         * multipleServiceRegistry.register(registration);
+         * }
+ * + * @param registriesMap a map of bean names to {@link ServiceRegistry} instances + * @return a new {@link MultipleServiceRegistry} delegating to all registries + */ @Bean @Primary @ConditionalOnMissingBean @@ -69,6 +98,23 @@ public MultipleServiceRegistry multipleServiceRegistry(MapExample Usage: + *
{@code
+         * @Autowired
+         * MultipleAutoServiceRegistration autoRegistration;
+         * // Auto-registration is managed by the Spring lifecycle
+         * boolean running = autoRegistration.isRunning();
+         * }
+ * + * @param multipleRegistration the composite {@link MultipleRegistration} + * @param multipleServiceRegistry the composite {@link MultipleServiceRegistry} + * @param properties the {@link AutoServiceRegistrationProperties} + * @return a new {@link MultipleAutoServiceRegistration} instance + */ @ConditionalOnBean(AutoServiceRegistrationProperties.class) @Primary @Bean diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java index 7453eef1..c3028236 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java @@ -80,6 +80,22 @@ public class SimpleAutoServiceRegistrationAutoConfiguration { ) public static final String ENABLED_PROPERTY_NAME = PROPERTY_NAME_PREFIX + PropertyConstants.ENABLED_PROPERTY_NAME; + /** + * Creates a {@link Registration} bean from the application name, server properties, and network info. + * + *

Example Usage: + *

{@code
+     * // Auto-configured via Spring Boot; the bean is available for injection:
+     * @Autowired
+     * Registration registration;
+     * String serviceId = registration.getServiceId();
+     * }
+ * + * @param applicationName the Spring application name resolved from {@code spring.application.name} + * @param serverProperties the {@link ServerProperties} providing the server port + * @param inetUtils the {@link InetUtils} for resolving the host address + * @return a new {@link DefaultRegistration} instance + */ @Bean public Registration registration( @Value("${spring.application.name:default}") String applicationName, @@ -98,6 +114,19 @@ public Registration registration( return registration; } + /** + * Creates an {@link InMemoryServiceRegistry} bean as the default {@link ServiceRegistry} implementation. + * + *

Example Usage: + *

{@code
+     * // Auto-configured when no other ServiceRegistry bean is present:
+     * @Autowired
+     * ServiceRegistry serviceRegistry;
+     * serviceRegistry.register(registration);
+     * }
+ * + * @return a new {@link InMemoryServiceRegistry} instance + */ @Bean @ConditionalOnMissingBean public ServiceRegistry serviceRegistry() { diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java index 4dbdff1e..87ede268 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java @@ -31,11 +31,37 @@ @ConditionalOnWebApplication(type = REACTIVE) public class WebFluxServiceRegistryAutoConfiguration extends WebServiceRegistryAutoConfiguration { + /** + * {@inheritDoc} + *

Returns an empty string as WebFlux applications do not use a servlet context path. + * + *

Example Usage: + *

{@code
+     * WebFluxServiceRegistryAutoConfiguration config = new WebFluxServiceRegistryAutoConfiguration();
+     * String contextPath = config.getContextPath(); // returns ""
+     * }
+ * + * @return an empty string + */ @Override protected String getContextPath() { return ""; } + /** + * {@inheritDoc} + *

Always returns {@code false} for WebFlux applications, as no mappings are excluded. + * + *

Example Usage: + *

{@code
+     * WebFluxServiceRegistryAutoConfiguration config = new WebFluxServiceRegistryAutoConfiguration();
+     * boolean excluded = config.isExcludedMapping(mapping, patterns); // always false
+     * }
+ * + * @param mapping the {@link WebEndpointMapping} to evaluate + * @param patterns the URL patterns associated with the mapping + * @return always {@code false} + */ @Override protected boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns) { return false; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index 1a8fc0d7..b160d563 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -55,11 +55,37 @@ public class WebMvcServiceRegistryAutoConfiguration extends WebServiceRegistryAu @Autowired private ObjectProvider dispatcherServletRegistrationBeanProvider; + /** + * {@inheritDoc} + *

Returns the servlet context path configured via {@code server.servlet.context-path}. + * + *

Example Usage: + *

{@code
+     * // With application property: server.servlet.context-path=/api
+     * String contextPath = config.getContextPath(); // returns "/api"
+     * }
+ * + * @return the servlet context path + */ @Override protected String getContextPath() { return this.contextPath; } + /** + * {@inheritDoc} + *

Excludes built-in Spring filter mappings and the default DispatcherServlet mapping. + * + *

Example Usage: + *

{@code
+     * boolean excluded = config.isExcludedMapping(mapping, new String[]{"/*"});
+     * // returns true if the mapping matches a built-in filter or DispatcherServlet
+     * }
+ * + * @param mapping the {@link WebEndpointMapping} to evaluate + * @param patterns the URL patterns associated with the mapping + * @return {@code true} if the mapping is a built-in filter or DispatcherServlet mapping + */ @Override protected boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns) { return isBuiltInFilterMapping(patterns) || isDispatcherServletMapping(mapping, patterns); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java index e025a791..02f61c23 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java @@ -62,6 +62,19 @@ public abstract class WebServiceRegistryAutoConfiguration implements Application @Value("${management.endpoints.web.base-path:/actuator}") protected String actuatorBasePath; + /** + * Handles {@link WebEndpointMappingsReadyEvent} by attaching web endpoint mapping metadata + * to all available {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * // This listener is invoked automatically by the Spring event system:
+     * // When WebEndpointMappingsReadyEvent is published, metadata is attached
+     * // to each Registration bean in the ApplicationContext.
+     * }
+ * + * @param event the {@link WebEndpointMappingsReadyEvent} containing the web endpoint mappings + */ @Override public final void onApplicationEvent(WebEndpointMappingsReadyEvent event) { ApplicationContext context = event.getApplicationContext(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java index 5c25b8bb..c9737de9 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java @@ -46,6 +46,17 @@ public abstract class AbstractServiceRegistrationEndpoint implements SmartInitia protected static boolean running; + /** + * {@inheritDoc} + *

Initializes the {@link Registration}, {@link ServiceRegistry}, and + * {@link AbstractAutoServiceRegistration} from available bean providers. + * + *

Example Usage: + *

{@code
+     * // Called automatically by the Spring container after all singletons are instantiated.
+     * // Ensures registration, serviceRegistry, and serviceRegistration fields are populated.
+     * }
+ */ @Override public void afterSingletonsInstantiated() { this.registration = registrationProvider.getIfAvailable(); @@ -53,6 +64,19 @@ public void afterSingletonsInstantiated() { this.serviceRegistration = autoServiceRegistrationProvider.getIfAvailable(); } + /** + * {@inheritDoc} + *

Captures the web server port and detects the running state of the + * {@link AbstractAutoServiceRegistration}. + * + *

Example Usage: + *

{@code
+     * // Called automatically when the embedded web server has been initialized.
+     * // After this event, the port and running state are available.
+     * }
+ * + * @param event the {@link WebServerInitializedEvent} carrying the initialized web server + */ @Override public void onApplicationEvent(WebServerInitializedEvent event) { WebServer webServer = event.getWebServer(); @@ -60,14 +84,48 @@ public void onApplicationEvent(WebServerInitializedEvent event) { this.running = detectRunning(serviceRegistration); } + /** + * Detects whether the given {@link AbstractAutoServiceRegistration} is currently running. + * + *

Example Usage: + *

{@code
+     * boolean running = AbstractServiceRegistrationEndpoint.detectRunning(serviceRegistration);
+     * }
+ * + * @param serviceRegistration the {@link AbstractAutoServiceRegistration} to check, may be {@code null} + * @return {@code true} if the service registration is running, {@code false} otherwise + */ static boolean detectRunning(AbstractAutoServiceRegistration serviceRegistration) { return serviceRegistration == null ? false : serviceRegistration.isRunning(); } + /** + * Returns whether the service registration is currently running. + * + *

Example Usage: + *

{@code
+     * if (endpoint.isRunning()) {
+     *     // service is registered and running
+     * }
+     * }
+ * + * @return {@code true} if the service registration is running, {@code false} otherwise + */ protected boolean isRunning() { return running; } + /** + * Sets the running state of the service registration. + * + *

Example Usage: + *

{@code
+     * endpoint.setRunning(true);  // mark service as running
+     * endpoint.setRunning(false); // mark service as stopped
+     * }
+ * + * @param running {@code true} to mark the service as running, {@code false} otherwise + */ public void setRunning(boolean running) { this.running = running; } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java index a3a5e418..e9858f1d 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java @@ -15,6 +15,19 @@ @Endpoint(id = "serviceDeregistration") public class ServiceDeregistrationEndpoint extends AbstractServiceRegistrationEndpoint { + /** + * Deregisters the service from the {@link ServiceRegistry} if it is currently running. + * This is a write operation exposed via the {@code /actuator/serviceDeregistration} endpoint. + * + *

Example Usage: + *

{@code
+     * // Via actuator HTTP POST to /actuator/serviceDeregistration
+     * ServiceDeregistrationEndpoint endpoint = context.getBean(ServiceDeregistrationEndpoint.class);
+     * boolean wasRunning = endpoint.stop();
+     * }
+ * + * @return {@code true} if the service was running before deregistration, {@code false} otherwise + */ @WriteOperation public boolean stop() { boolean isRunning = isRunning(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java index 2fafbff8..afc898ad 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java @@ -22,6 +22,21 @@ @Endpoint(id = "serviceRegistration") public class ServiceRegistrationEndpoint extends AbstractServiceRegistrationEndpoint { + /** + * Returns metadata about the current service registration, including application name, + * registration details, port, status, and running state. + * This is a read operation exposed via the {@code /actuator/serviceRegistration} endpoint. + * + *

Example Usage: + *

{@code
+     * // Via actuator HTTP GET to /actuator/serviceRegistration
+     * ServiceRegistrationEndpoint endpoint = context.getBean(ServiceRegistrationEndpoint.class);
+     * Map metadata = endpoint.metadata();
+     * String appName = (String) metadata.get("application-name");
+     * }
+ * + * @return a {@link Map} containing service registration metadata + */ @ReadOperation public Map metadata() { Map metadata = new LinkedHashMap<>(16); @@ -38,6 +53,19 @@ public Map metadata() { return metadata; } + /** + * Registers the service with the {@link ServiceRegistry} if it is not already running. + * This is a write operation exposed via the {@code /actuator/serviceRegistration} endpoint. + * + *

Example Usage: + *

{@code
+     * // Via actuator HTTP POST to /actuator/serviceRegistration
+     * ServiceRegistrationEndpoint endpoint = context.getBean(ServiceRegistrationEndpoint.class);
+     * boolean wasAlreadyRunning = endpoint.start();
+     * }
+ * + * @return {@code true} if the service was already running, {@code false} if it was newly registered + */ @WriteOperation public boolean start() { boolean isRunning = isRunning(); From e733ebc13f05f326f3ab72d8325f54f82208ff3b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:04:51 +0000 Subject: [PATCH 285/310] Add JavaDoc with Example Usage sections to non-private methods missing documentation Add JavaDoc comments to constructors, getters, setters, and other non-private methods across 7 files in the registry event and fault tolerance packages. Each JavaDoc follows the project style with brief descriptions, {@link} cross-references, Example Usage sections using
{@code}
blocks, and @param/@return tags where applicable. Files updated: - RegistrationDeregisteredEvent.java - RegistrationPreDeregisteredEvent.java - RegistrationPreRegisteredEvent.java - RegistrationRegisteredEvent.java - WeightedRoundRobin.java - TomcatFaultToleranceAutoConfiguration.java - TomcatDynamicConfigurationListener.java Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../event/RegistrationDeregisteredEvent.java | 27 +++++ .../RegistrationPreDeregisteredEvent.java | 27 +++++ .../event/RegistrationPreRegisteredEvent.java | 27 +++++ .../event/RegistrationRegisteredEvent.java | 27 +++++ .../loadbalancer/WeightedRoundRobin.java | 109 ++++++++++++++++++ ...TomcatFaultToleranceAutoConfiguration.java | 12 ++ .../TomcatDynamicConfigurationListener.java | 73 ++++++++++++ 7 files changed, 302 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java index de176582..45ef54ce 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationDeregisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationDeregisteredEvent} indicating that a + * {@link Registration} has been deregistered from the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationDeregisteredEvent event = new RegistrationDeregisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that performed the deregistration + * @param source the {@link Registration} that was deregistered + */ public RegistrationDeregisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#DEREGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationDeregisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.DEREGISTERED
+     * }
+ * + * @return {@link Type#DEREGISTERED} + */ @Override public Type getType() { return DEREGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java index 3ef61b67..b98fc529 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationPreDeregisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationPreDeregisteredEvent} indicating that a + * {@link Registration} is about to be deregistered from the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationPreDeregisteredEvent event = new RegistrationPreDeregisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that will perform the deregistration + * @param source the {@link Registration} to be deregistered + */ public RegistrationPreDeregisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#PRE_DEREGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationPreDeregisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.PRE_DEREGISTERED
+     * }
+ * + * @return {@link Type#PRE_DEREGISTERED} + */ @Override public Type getType() { return PRE_DEREGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java index 64073258..8ae5c353 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationPreRegisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationPreRegisteredEvent} indicating that a + * {@link Registration} is about to be registered with the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationPreRegisteredEvent event = new RegistrationPreRegisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that will perform the registration + * @param source the {@link Registration} to be registered + */ public RegistrationPreRegisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#PRE_REGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationPreRegisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.PRE_REGISTERED
+     * }
+ * + * @return {@link Type#PRE_REGISTERED} + */ @Override public Type getType() { return PRE_REGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java index 06306ac8..42932dc7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationRegisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationRegisteredEvent} indicating that a + * {@link Registration} has been registered with the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationRegisteredEvent event = new RegistrationRegisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that performed the registration + * @param source the {@link Registration} that was registered + */ public RegistrationRegisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#REGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationRegisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.REGISTERED
+     * }
+ * + * @return {@link Type#REGISTERED} + */ @Override public Type getType() { return REGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java index dc2f30d8..b2991743 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java @@ -19,40 +19,149 @@ public class WeightedRoundRobin { private volatile long lastUpdate; + /** + * Create a new {@link WeightedRoundRobin} instance with the given identifier. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * }
+ * + * @param id the unique identifier for this weighted round-robin entry + */ public WeightedRoundRobin(String id) { this.id = id; } + /** + * Get the unique identifier for this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * String id = wrr.getId(); // "server-1"
+     * }
+ * + * @return the identifier + */ public String getId() { return id; } + /** + * Get the current weight of this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * int weight = wrr.getWeight(); // 5
+     * }
+ * + * @return the current weight + */ public int getWeight() { return weight; } + /** + * Set the weight for this {@link WeightedRoundRobin} entry and reset the current counter. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(10);
+     * }
+ * + * @param weight the new weight value + */ public void setWeight(int weight) { this.weight = weight; current.reset(); } + /** + * Increase the current counter by the weight value and return the updated value. + * Used during weighted round-robin selection to accumulate the weight for this entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * long current = wrr.increaseCurrent(); // 5
+     * current = wrr.increaseCurrent();      // 10
+     * }
+ * + * @return the updated current counter value + */ public long increaseCurrent() { current.add(weight); return current.longValue(); } + /** + * Subtract the total weight from the current counter after this entry has been selected. + * This is part of the weighted round-robin algorithm to reduce the selected entry's counter. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * wrr.increaseCurrent();
+     * wrr.sel(10); // subtract total weight of all entries
+     * }
+ * + * @param total the total weight of all entries to subtract + */ public void sel(int total) { current.add(-1 * total); } + /** + * Get the timestamp of the last update to this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setLastUpdate(System.currentTimeMillis());
+     * long lastUpdate = wrr.getLastUpdate();
+     * }
+ * + * @return the last update timestamp in milliseconds + */ public long getLastUpdate() { return lastUpdate; } + /** + * Set the timestamp of the last update to this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setLastUpdate(System.currentTimeMillis());
+     * }
+ * + * @param lastUpdate the last update timestamp in milliseconds + */ public void setLastUpdate(long lastUpdate) { this.lastUpdate = lastUpdate; } + /** + * Returns a string representation of this {@link WeightedRoundRobin} including + * its id, weight, current counter, and last update timestamp. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * String s = wrr.toString(); // "WeightedRoundRobin[id='server-1', weight=5, current=0, lastUpdate=0]"
+     * }
+ * + * @return a string representation of this entry + */ @Override public String toString() { return new StringJoiner(", ", WeightedRoundRobin.class.getSimpleName() + "[", "]") diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java index 58b1cd92..b32ad811 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java @@ -72,6 +72,18 @@ public class TomcatFaultToleranceAutoConfiguration { ) public static final String ENABLED_PROPERTY_NAME = TOMCAT_PROPERTY_PREFIX + "." + PropertyConstants.ENABLED_PROPERTY_NAME; + /** + * Handles the {@link WebServerInitializedEvent} to register a {@link TomcatDynamicConfigurationListener} + * when the embedded web server is a {@link TomcatWebServer}. + * + *

Example Usage: + *

{@code
+     * // Automatically invoked by Spring when WebServerInitializedEvent is published.
+     * // The listener is registered as an ApplicationListener on the web application context.
+     * }
+ * + * @param event the {@link WebServerInitializedEvent} triggered after the web server starts + */ @EventListener(WebServerInitializedEvent.class) public void onWebServerInitializedEvent(WebServerInitializedEvent event) { WebServerApplicationContext webServerApplicationContext = event.getApplicationContext(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java index 10daa3c4..42abdb41 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java @@ -68,6 +68,25 @@ public class TomcatDynamicConfigurationListener implements ApplicationListenerExample Usage: + *
{@code
+     * TomcatWebServer tomcatWebServer = ...;
+     * ServerProperties serverProperties = ...;
+     * ConfigurableApplicationContext context = ...;
+     * TomcatDynamicConfigurationListener listener =
+     *     new TomcatDynamicConfigurationListener(tomcatWebServer, serverProperties, context);
+     * context.addApplicationListener(listener);
+     * }
+ * + * @param tomcatWebServer the {@link TomcatWebServer} to reconfigure dynamically + * @param serverProperties the current {@link ServerProperties} + * @param context the {@link ConfigurableApplicationContext} for environment access + */ public TomcatDynamicConfigurationListener(TomcatWebServer tomcatWebServer, ServerProperties serverProperties, ConfigurableApplicationContext context) { this.tomcatWebServer = tomcatWebServer; @@ -85,6 +104,18 @@ private void initCurrentServerProperties() { this.currentServerProperties = getCurrentServerProperties(environment); } + /** + * Handles an {@link EnvironmentChangeEvent} by reconfiguring the Tomcat connector + * if any server-related properties have changed. + * + *

Example Usage: + *

{@code
+     * // Automatically invoked by Spring when an EnvironmentChangeEvent is published.
+     * // Reconfigures Tomcat settings such as thread pool size, connection timeout, etc.
+     * }
+ * + * @param event the {@link EnvironmentChangeEvent} containing the changed property keys + */ @Override public void onApplicationEvent(EnvironmentChangeEvent event) { if (!isSourceFrom(event)) { @@ -146,6 +177,20 @@ private void configureConnector(ServerProperties refreshableServerProperties) { configureHttp11Protocol(refreshableServerProperties, connector, protocolHandler); } + /** + * Configure the Tomcat {@link AbstractProtocol} settings such as thread pool sizes, + * accept count, connection timeout, and max connections from the refreshed {@link ServerProperties}. + * + *

Example Usage: + *

{@code
+     * ServerProperties refreshedProperties = ...;
+     * ProtocolHandler protocolHandler = connector.getProtocolHandler();
+     * listener.configureProtocol(refreshedProperties, protocolHandler);
+     * }
+ * + * @param refreshableServerProperties the refreshed {@link ServerProperties} to apply + * @param protocolHandler the {@link ProtocolHandler} to configure + */ void configureProtocol(ServerProperties refreshableServerProperties, ProtocolHandler protocolHandler) { if (protocolHandler instanceof AbstractProtocol) { @@ -194,6 +239,22 @@ void configureProtocol(ServerProperties refreshableServerProperties, ProtocolHan } } + /** + * Configure the Tomcat {@link AbstractHttp11Protocol} settings such as max HTTP header size, + * max swallow size, and max HTTP form POST size from the refreshed {@link ServerProperties}. + * + *

Example Usage: + *

{@code
+     * ServerProperties refreshedProperties = ...;
+     * Connector connector = tomcatWebServer.getTomcat().getConnector();
+     * ProtocolHandler protocolHandler = connector.getProtocolHandler();
+     * listener.configureHttp11Protocol(refreshedProperties, connector, protocolHandler);
+     * }
+ * + * @param refreshableServerProperties the refreshed {@link ServerProperties} to apply + * @param connector the Tomcat {@link Connector} + * @param protocolHandler the {@link ProtocolHandler} to configure + */ void configureHttp11Protocol(ServerProperties refreshableServerProperties, Connector connector, ProtocolHandler protocolHandler) { if (protocolHandler instanceof AbstractHttp11Protocol) { AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) protocolHandler; @@ -236,6 +297,18 @@ private int toIntBytes(DataSize dataSize) { return (int) dataSize.toBytes(); } + /** + * Check whether the given integer value is positive (greater than zero). + * + *

Example Usage: + *

{@code
+     * boolean result = listener.isPositive(10);  // true
+     * boolean result2 = listener.isPositive(0);  // false
+     * }
+ * + * @param value the value to check + * @return {@code true} if the value is greater than zero + */ boolean isPositive(int value) { return value > 0; } From 99f5660ceb7ba5cdac33714532800c5094d66c28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:10:51 +0000 Subject: [PATCH 286/310] Add JavaDoc with Example Usage sections to non-private methods in openfeign module Add comprehensive JavaDoc comments including brief descriptions, @link cross-references, Example Usage sections with
{@code ...}
blocks, and @param/@return tags to all non-private methods that were missing documentation across 14 files in the openfeign module. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...ignClientAutoRefreshAutoConfiguration.java | 51 +++++++ ...FeignClientSpecificationPostProcessor.java | 15 ++ .../autorefresh/AutoRefreshCapability.java | 117 +++++++++++++++ ...ignClientConfigurationChangedListener.java | 37 +++++ .../autorefresh/FeignComponentRegistry.java | 127 ++++++++++++++++ .../CompositedRequestInterceptor.java | 99 +++++++++++++ .../components/DecoratedContract.java | 37 +++++ .../components/DecoratedDecoder.java | 41 ++++++ .../components/DecoratedEncoder.java | 39 +++++ .../components/DecoratedErrorDecoder.java | 37 +++++ .../components/DecoratedFeignComponent.java | 138 ++++++++++++++++++ .../components/DecoratedQueryMapEncoder.java | 51 +++++++ .../components/DecoratedRetryer.java | 61 ++++++++ .../components/NoOpRequestInterceptor.java | 21 +++ 14 files changed, 871 insertions(+) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java index 46f2e3d5..7e851e56 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java @@ -23,6 +23,18 @@ @ConditionalOnBean(EnableFeignAutoRefresh.Marker.class) public class FeignClientAutoRefreshAutoConfiguration { + /** + * Creates a {@link FeignBuilderCustomizer} that adds the {@link NoOpRequestInterceptor} + * as a default request interceptor to every Feign client builder. + * + *

Example Usage: + *

{@code
+     * // Automatically registered as a Spring bean; customizes every Feign builder
+     * FeignBuilderCustomizer customizer = addDefaultRequestInterceptorCustomizer();
+     * }
+ * + * @return a {@link FeignBuilderCustomizer} that adds the {@link NoOpRequestInterceptor} + */ @Bean public FeignBuilderCustomizer addDefaultRequestInterceptorCustomizer() { return builder -> { @@ -30,6 +42,18 @@ public FeignBuilderCustomizer addDefaultRequestInterceptorCustomizer() { }; } + /** + * Handles the {@link ApplicationReadyEvent} to register the + * {@link FeignClientConfigurationChangedListener} after the application is fully initialized. + * + *

Example Usage: + *

{@code
+     * // Invoked automatically by the Spring event system on application ready
+     * onApplicationReadyEvent(applicationReadyEvent);
+     * }
+ * + * @param event the {@link ApplicationReadyEvent} fired when the application is ready + */ @EventListener(ApplicationReadyEvent.class) public void onApplicationReadyEvent(ApplicationReadyEvent event) { /** @@ -38,11 +62,38 @@ public void onApplicationReadyEvent(ApplicationReadyEvent event) { registerFeignClientConfigurationChangedListener(event); } + /** + * Creates the {@link FeignComponentRegistry} bean that tracks decorated Feign components + * and supports auto-refresh when configuration properties change. + * + *

Example Usage: + *

{@code
+     * // Automatically registered as a Spring bean
+     * FeignComponentRegistry registry = feignClientRegistry(clientProperties, beanFactory);
+     * }
+ * + * @param clientProperties the {@link FeignClientProperties} providing the default config name + * @param beanFactory the {@link BeanFactory} used for component instantiation + * @return a new {@link FeignComponentRegistry} instance + */ @Bean public FeignComponentRegistry feignClientRegistry(FeignClientProperties clientProperties, BeanFactory beanFactory) { return new FeignComponentRegistry(clientProperties.getDefaultConfig(), beanFactory); } + /** + * Creates the {@link FeignClientSpecificationPostProcessor} bean that injects + * the {@link io.microsphere.spring.cloud.openfeign.autorefresh.AutoRefreshCapability} + * into default Feign client specifications. + * + *

Example Usage: + *

{@code
+     * // Automatically registered as a Spring bean
+     * FeignClientSpecificationPostProcessor processor = feignClientSpecificationPostProcessor();
+     * }
+ * + * @return a new {@link FeignClientSpecificationPostProcessor} instance + */ @Bean public FeignClientSpecificationPostProcessor feignClientSpecificationPostProcessor() { return new FeignClientSpecificationPostProcessor(); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java index 1dd44c8d..895c3bc2 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java @@ -27,6 +27,21 @@ public class FeignClientSpecificationPostProcessor implements BeanPostProcessor private static final Class FEIGN_CLIENT_SPECIFICATION_CLASS = FeignClientSpecification.class; + /** + * Injects the {@link AutoRefreshCapability} into default {@link FeignClientSpecification} + * beans after initialization. + * + *

Example Usage: + *

{@code
+     * // Invoked automatically by the Spring container during bean post-processing
+     * Object processed = postProcessAfterInitialization(bean, "default.my-client");
+     * }
+ * + * @param bean the bean instance that has been initialized + * @param beanName the name of the bean in the Spring context + * @return the (possibly modified) bean instance + * @throws BeansException if post-processing fails + */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class beanType = getTargetClass(bean); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index ebc5b9f2..d9dc7ddd 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -38,6 +38,19 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAwar private String contextId; + /** + * Constructs an {@link AutoRefreshCapability} with the required dependencies. + * + *

Example Usage: + *

{@code
+     * AutoRefreshCapability capability = new AutoRefreshCapability(
+     *     clientProperties, contextFactory, componentRegistry);
+     * }
+ * + * @param clientProperties the {@link FeignClientProperties} providing Feign client configuration + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param componentRegistry the {@link FeignComponentRegistry} to register decorated components + */ public AutoRefreshCapability(FeignClientProperties clientProperties, NamedContextFactory contextFactory, FeignComponentRegistry componentRegistry) { @@ -46,11 +59,37 @@ public AutoRefreshCapability(FeignClientProperties clientProperties, this.componentRegistry = componentRegistry; } + /** + * Sets the {@link ApplicationContext} and extracts the Feign client context ID + * from the {@code spring.cloud.openfeign.client.name} property. + * + *

Example Usage: + *

{@code
+     * AutoRefreshCapability capability = new AutoRefreshCapability(props, factory, registry);
+     * capability.setApplicationContext(applicationContext);
+     * }
+ * + * @param applicationContext the {@link ApplicationContext} for this Feign client + * @throws BeansException if the context cannot be set + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.contextId = applicationContext.getEnvironment().getProperty("spring.cloud.openfeign.client.name"); } + /** + * Enriches the given {@link Retryer} by wrapping it in a {@link DecoratedRetryer} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Retryer original = new Retryer.Default();
+     * Retryer enriched = capability.enrich(original);
+     * }
+ * + * @param retryer the original {@link Retryer} to enrich, or {@code null} + * @return the decorated {@link Retryer}, or {@code null} if the input is {@code null} + */ @Override public Retryer enrich(Retryer retryer) { if (retryer == null) { @@ -63,6 +102,19 @@ public Retryer enrich(Retryer retryer) { return decoratedRetryer; } + /** + * Enriches the given {@link Contract} by wrapping it in a {@link DecoratedContract} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Contract original = new Contract.Default();
+     * Contract enriched = capability.enrich(original);
+     * }
+ * + * @param contract the original {@link Contract} to enrich, or {@code null} + * @return the decorated {@link Contract}, or {@code null} if the input is {@code null} + */ @Override public Contract enrich(Contract contract) { if (contract == null) { @@ -75,6 +127,19 @@ public Contract enrich(Contract contract) { return decoratedContract; } + /** + * Enriches the given {@link Decoder} by wrapping it in a {@link DecoratedDecoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Decoder original = new Decoder.Default();
+     * Decoder enriched = capability.enrich(original);
+     * }
+ * + * @param decoder the original {@link Decoder} to enrich, or {@code null} + * @return the decorated {@link Decoder}, or {@code null} if the input is {@code null} + */ @Override public Decoder enrich(Decoder decoder) { if (decoder == null) { @@ -87,6 +152,19 @@ public Decoder enrich(Decoder decoder) { return decoratedDecoder; } + /** + * Enriches the given {@link Encoder} by wrapping it in a {@link DecoratedEncoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Encoder original = new Encoder.Default();
+     * Encoder enriched = capability.enrich(original);
+     * }
+ * + * @param encoder the original {@link Encoder} to enrich, or {@code null} + * @return the decorated {@link Encoder}, or {@code null} if the input is {@code null} + */ @Override public Encoder enrich(Encoder encoder) { if (encoder == null) { @@ -99,6 +177,19 @@ public Encoder enrich(Encoder encoder) { return decoratedEncoder; } + /** + * Enriches the given {@link ErrorDecoder} by wrapping it in a {@link DecoratedErrorDecoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * ErrorDecoder original = new ErrorDecoder.Default();
+     * ErrorDecoder enriched = capability.enrich(original);
+     * }
+ * + * @param decoder the original {@link ErrorDecoder} to enrich, or {@code null} + * @return the decorated {@link ErrorDecoder}, or {@code null} if the input is {@code null} + */ public ErrorDecoder enrich(ErrorDecoder decoder) { if (decoder == null) { return null; @@ -110,6 +201,19 @@ public ErrorDecoder enrich(ErrorDecoder decoder) { return decoratedErrorDecoder; } + /** + * Enriches the given {@link RequestInterceptor} by registering it in the + * {@link FeignComponentRegistry} as part of a {@link io.microsphere.spring.cloud.openfeign.components.CompositedRequestInterceptor}. + * + *

Example Usage: + *

{@code
+     * RequestInterceptor original = template -> template.header("X-Custom", "value");
+     * RequestInterceptor enriched = capability.enrich(original);
+     * }
+ * + * @param requestInterceptor the original {@link RequestInterceptor} to enrich, or {@code null} + * @return the composited {@link RequestInterceptor}, or {@code null} if the input is {@code null} + */ @Override public RequestInterceptor enrich(RequestInterceptor requestInterceptor) { if (requestInterceptor == null) { @@ -118,6 +222,19 @@ public RequestInterceptor enrich(RequestInterceptor requestInterceptor) { return this.componentRegistry.registerRequestInterceptor(contextId, requestInterceptor); } + /** + * Enriches the given {@link QueryMapEncoder} by wrapping it in a {@link DecoratedQueryMapEncoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * QueryMapEncoder original = new QueryMapEncoder.Default();
+     * QueryMapEncoder enriched = capability.enrich(original);
+     * }
+ * + * @param queryMapEncoder the original {@link QueryMapEncoder} to enrich, or {@code null} + * @return the decorated {@link QueryMapEncoder}, or {@code null} if the input is {@code null} + */ @Override public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { if (queryMapEncoder == null) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java index 835245b8..7fae5c63 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java @@ -15,12 +15,36 @@ public class FeignClientConfigurationChangedListener implements ApplicationListe private final FeignComponentRegistry registry; + /** + * Constructs a listener that refreshes Feign components when the environment changes. + * + *

Example Usage: + *

{@code
+     * FeignComponentRegistry registry = ...;
+     * FeignClientConfigurationChangedListener listener =
+     *     new FeignClientConfigurationChangedListener(registry);
+     * }
+ * + * @param registry the {@link FeignComponentRegistry} used to refresh affected Feign components + */ public FeignClientConfigurationChangedListener(FeignComponentRegistry registry) { this.registry = registry; } private final String PREFIX = "spring.cloud.openfeign.client.config."; + /** + * Handles an {@link EnvironmentChangeEvent} by resolving which Feign clients are affected + * and triggering a refresh on the corresponding components in the registry. + * + *

Example Usage: + *

{@code
+     * // Invoked automatically by the Spring event system
+     * listener.onApplicationEvent(environmentChangeEvent);
+     * }
+ * + * @param event the {@link EnvironmentChangeEvent} containing the changed property keys + */ @Override public void onApplicationEvent(EnvironmentChangeEvent event) { Map> effectiveClients = resolveChangedClient(event); @@ -29,6 +53,19 @@ public void onApplicationEvent(EnvironmentChangeEvent event) { } } + /** + * Resolves which Feign client names and their changed configuration keys are affected + * by the given {@link EnvironmentChangeEvent}. + * + *

Example Usage: + *

{@code
+     * Map> changed = listener.resolveChangedClient(event);
+     * // e.g. {"my-client" -> {"retryer", "decoder"}}
+     * }
+ * + * @param event the {@link EnvironmentChangeEvent} containing the changed property keys + * @return a map of client names to their changed configuration sub-keys + */ protected Map> resolveChangedClient(EnvironmentChangeEvent event) { Set keys = event.getKeys(); return keys.stream() diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java index 45b1d84c..4c20fae0 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java @@ -60,6 +60,18 @@ public class FeignComponentRegistry { private final BeanFactory beanFactory; + /** + * Returns the Feign component class corresponding to the given configuration key. + * + *

Example Usage: + *

{@code
+     * Class componentClass = FeignComponentRegistry.getComponentClass("retryer");
+     * // returns Retryer.class
+     * }
+ * + * @param config the configuration property key (e.g. {@code "retryer"}, {@code "decoder"}) + * @return the mapped Feign component {@link Class}, or {@code null} if not found + */ protected static Class getComponentClass(String config) { if (isBlank(config)) { return null; @@ -68,16 +80,53 @@ protected static Class getComponentClass(String config) { return configComponentMappings.get(normalizedConfig); } + /** + * Normalizes a configuration key by stripping array index suffixes and converting + * to dashed form. + * + *

Example Usage: + *

{@code
+     * String normalized = FeignComponentRegistry.normalizeConfig("requestInterceptors[0]");
+     * // returns "request-interceptors"
+     * }
+ * + * @param config the raw configuration property key + * @return the normalized, dashed-form configuration key + */ static String normalizeConfig(String config) { String normalizedConfig = substringBefore(config, LEFT_SQUARE_BRACKET); return toDashedForm(normalizedConfig); } + /** + * Constructs a {@link FeignComponentRegistry} with the given default client name + * and {@link BeanFactory}. + * + *

Example Usage: + *

{@code
+     * FeignComponentRegistry registry = new FeignComponentRegistry("default", beanFactory);
+     * }
+ * + * @param defaultClientName the name of the default Feign client configuration + * @param beanFactory the {@link BeanFactory} used for component resolution + */ public FeignComponentRegistry(String defaultClientName, BeanFactory beanFactory) { this.defaultClientName = defaultClientName; this.beanFactory = beanFactory; } + /** + * Registers a list of {@link Refreshable} components for the specified Feign client. + * + *

Example Usage: + *

{@code
+     * List components = List.of(decoratedContract, decoratedDecoder);
+     * registry.register("my-client", components);
+     * }
+ * + * @param clientName the Feign client name + * @param components the list of {@link Refreshable} components to register + */ public void register(String clientName, List components) { assertNotBlank(clientName, () -> "The 'clientName' must not be blank!"); assertNotEmpty(components, () -> "The 'components' must not be empty!"); @@ -86,10 +135,36 @@ public void register(String clientName, List components) { componentList.addAll(components); } + /** + * Registers a single {@link Refreshable} component for the specified Feign client. + * + *

Example Usage: + *

{@code
+     * registry.register("my-client", decoratedContract);
+     * }
+ * + * @param clientName the Feign client name + * @param component the {@link Refreshable} component to register + */ public void register(String clientName, Refreshable component) { register(clientName, ofList(component)); } + /** + * Registers a {@link RequestInterceptor} for the specified Feign client. Interceptors + * are collected into a {@link CompositedRequestInterceptor} per client. + * + *

Example Usage: + *

{@code
+     * RequestInterceptor interceptor = template -> template.header("X-Custom", "value");
+     * RequestInterceptor result = registry.registerRequestInterceptor("my-client", interceptor);
+     * }
+ * + * @param clientName the Feign client name + * @param requestInterceptor the {@link RequestInterceptor} to register + * @return the {@link CompositedRequestInterceptor} if this is the first interceptor + * for the client, or {@link io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor#INSTANCE} otherwise + */ public RequestInterceptor registerRequestInterceptor(String clientName, RequestInterceptor requestInterceptor) { assertNotBlank(clientName, () -> "The 'clientName' must not be blank!"); assertNotNull(requestInterceptor, () -> "The 'requestInterceptor' must not be null!"); @@ -101,10 +176,35 @@ public RequestInterceptor registerRequestInterceptor(String clientName, RequestI } + /** + * Refreshes the Feign components for the specified client whose configurations have changed. + * + *

Example Usage: + *

{@code
+     * registry.refresh("my-client", "retryer", "decoder");
+     * }
+ * + * @param clientName the Feign client name + * @param changedConfigs the configuration keys that have changed + */ public void refresh(String clientName, String... changedConfigs) { refresh(clientName, ofSet(changedConfigs)); } + /** + * Refreshes the Feign components for the specified client based on a set of changed + * configuration keys. If the default client configuration changed, all registered + * components are refreshed. + * + *

Example Usage: + *

{@code
+     * Set changed = Set.of("my-client.retryer", "my-client.decoder");
+     * registry.refresh("my-client", changed);
+     * }
+ * + * @param clientName the Feign client name + * @param changedConfigs the set of changed configuration sub-keys + */ public synchronized void refresh(String clientName, Set changedConfigs) { Set> effectiveComponents = new HashSet<>(changedConfigs.size()); @@ -144,10 +244,37 @@ public synchronized void refresh(String clientName, Set changedConfigs) } } + /** + * Checks whether the given {@link Refreshable} component's class is assignable from + * any of the effective component classes. + * + *

Example Usage: + *

{@code
+     * boolean present = FeignComponentRegistry.isComponentPresent(
+     *     refreshableComponent, List.of(Retryer.class, Decoder.class));
+     * }
+ * + * @param component the {@link Refreshable} component to check + * @param effectiveComponents the component classes to match against + * @return {@code true} if the component matches any of the effective classes + */ static boolean isComponentPresent(Refreshable component, Iterable> effectiveComponents) { return isComponentClassPresent(component.getClass(), effectiveComponents); } + /** + * Checks whether the given class is assignable from any of the effective component classes. + * + *

Example Usage: + *

{@code
+     * boolean present = FeignComponentRegistry.isComponentClassPresent(
+     *     DecoratedRetryer.class, List.of(Retryer.class));
+     * }
+ * + * @param componentsClass the class to check + * @param effectiveComponents the component classes to match against + * @return {@code true} if the class is assignable from any effective class + */ static boolean isComponentClassPresent(Class componentsClass, Iterable> effectiveComponents) { for (Class actualComponent : effectiveComponents) { if (actualComponent.isAssignableFrom(componentsClass)) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index 81f65d74..4f051ee3 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -33,15 +33,49 @@ public class CompositedRequestInterceptor implements RequestInterceptor, Refresh private final Set set = new LinkedHashSet<>(); + /** + * Constructs a {@link CompositedRequestInterceptor} for the specified Feign client context. + * + *

Example Usage: + *

{@code
+     * CompositedRequestInterceptor interceptor =
+     *     new CompositedRequestInterceptor("my-client", beanFactory);
+     * }
+ * + * @param contextId the Feign client context ID + * @param beanFactory the {@link BeanFactory} for resolving interceptor instances + */ public CompositedRequestInterceptor(String contextId, BeanFactory beanFactory) { this.beanFactory = beanFactory; this.contextId = contextId; } + /** + * Returns an unmodifiable view of the registered {@link RequestInterceptor} instances. + * + *

Example Usage: + *

{@code
+     * Set interceptors = compositedInterceptor.getRequestInterceptors();
+     * }
+ * + * @return an unmodifiable {@link Set} of registered request interceptors + */ public Set getRequestInterceptors() { return unmodifiableSet(set); } + /** + * Applies all registered {@link RequestInterceptor} instances to the given + * {@link RequestTemplate} in order. + * + *

Example Usage: + *

{@code
+     * RequestTemplate template = new RequestTemplate();
+     * compositedInterceptor.apply(template);
+     * }
+ * + * @param template the {@link RequestTemplate} to apply interceptors to + */ @Override public void apply(RequestTemplate template) { synchronized (this.set) { @@ -49,6 +83,19 @@ public void apply(RequestTemplate template) { } } + /** + * Adds a {@link RequestInterceptor} to this composite. Returns {@code true} if this + * is the first interceptor added (i.e., the composite was previously empty). + * + *

Example Usage: + *

{@code
+     * boolean wasFirst = compositedInterceptor.addRequestInterceptor(
+     *     template -> template.header("Authorization", "Bearer token"));
+     * }
+ * + * @param requestInterceptor the {@link RequestInterceptor} to add + * @return {@code true} if this was the first interceptor added, {@code false} otherwise + */ public boolean addRequestInterceptor(RequestInterceptor requestInterceptor) { synchronized (this.set) { boolean isFirst = this.set.isEmpty(); @@ -61,6 +108,16 @@ private RequestInterceptor getInterceptorOrInstantiate(ClassExample Usage: + *
{@code
+     * compositedInterceptor.refresh();
+     * }
+ */ @Override public void refresh() { FeignClientProperties properties = getOrInstantiate(FeignClientProperties.class); @@ -110,6 +167,20 @@ public void refresh() { } } + /** + * Adds all elements from the source collection (obtained via the supplier) into the + * target collection if the source is not empty. + * + *

Example Usage: + *

{@code
+     * Collection target = new ArrayList<>();
+     * addAll(() -> List.of("a", "b"), target);
+     * }
+ * + * @param the element type + * @param sourceSupplier the supplier providing the source collection + * @param target the target collection to add elements to + */ static void addAll(Supplier> sourceSupplier, Collection target) { Collection source = sourceSupplier.get(); if (isNotEmpty(source)) { @@ -117,11 +188,39 @@ static void addAll(Supplier> sourceSupplier, Collection tar } } + /** + * Retrieves a bean of the given type from the {@link BeanFactory}, falling back to + * instantiation via the default constructor if the bean is not available. + * + *

Example Usage: + *

{@code
+     * FeignClientProperties properties = getOrInstantiate(FeignClientProperties.class);
+     * }
+ * + * @param the type of the bean + * @param clazz the class of the bean to retrieve or instantiate + * @return an instance of the requested type + */ T getOrInstantiate(Class clazz) { ObjectProvider beanProvider = this.beanFactory.getBeanProvider(clazz); return beanProvider.getIfAvailable(() -> instantiateClass(clazz)); } + /** + * Puts all entries from the source map (obtained via the supplier) into the target + * map if the source is not empty, without overwriting existing keys. + * + *

Example Usage: + *

{@code
+     * Map target = new HashMap<>();
+     * putIfAbsent(() -> Map.of("key", "value"), target);
+     * }
+ * + * @param the key type + * @param the value type + * @param sourceSupplier the supplier providing the source map + * @param target the target map to add entries to + */ static void putIfAbsent(Supplier> sourceSupplier, Map target) { Map source = sourceSupplier.get(); if (isNotEmpty(source)) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index 490fd88a..38ff43c8 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -16,16 +16,53 @@ */ public class DecoratedContract extends DecoratedFeignComponent implements Contract { + /** + * Constructs a {@link DecoratedContract} wrapping the given {@link Contract} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedContract contract = new DecoratedContract(
+     *     "my-client", contextFactory, clientProperties, new Contract.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Contract} to delegate to + */ public DecoratedContract(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Contract delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Contract} class from {@link FeignClientConfiguration}, + * falling back to {@link Contract} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedContract.componentType();
+     * }
+ * + * @return the {@link Contract} component type class + */ @Override protected Class componentType() { Class contractClass = get(FeignClientConfiguration::getContract); return contractClass == null ? Contract.class : contractClass; } + /** + * Parses and validates metadata for the given target type by delegating to the + * underlying {@link Contract}. + * + *

Example Usage: + *

{@code
+     * List metadata = decoratedContract.parseAndValidateMetadata(MyFeignClient.class);
+     * }
+ * + * @param targetType the Feign client interface class to parse + * @return the list of parsed {@link MethodMetadata} + */ @Override public List parseAndValidateMetadata(Class targetType) { return delegate().parseAndValidateMetadata(targetType); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index 9d3cb79c..21fb832e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -19,16 +19,57 @@ */ public class DecoratedDecoder extends DecoratedFeignComponent implements Decoder { + /** + * Constructs a {@link DecoratedDecoder} wrapping the given {@link Decoder} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedDecoder decoder = new DecoratedDecoder(
+     *     "my-client", contextFactory, clientProperties, new Decoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Decoder} to delegate to + */ public DecoratedDecoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Decoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Decoder} class from {@link FeignClientConfiguration}, + * falling back to {@link Decoder} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedDecoder.componentType();
+     * }
+ * + * @return the {@link Decoder} component type class + */ @Override protected Class componentType() { Class decoderClass = get(FeignClientConfiguration::getDecoder); return decoderClass == null ? Decoder.class : decoderClass; } + /** + * Decodes a Feign {@link Response} into an object of the given type by delegating + * to the underlying {@link Decoder}. + * + *

Example Usage: + *

{@code
+     * Object result = decoratedDecoder.decode(response, String.class);
+     * }
+ * + * @param response the Feign {@link Response} to decode + * @param type the target type to decode into + * @return the decoded object + * @throws IOException if an I/O error occurs + * @throws DecodeException if decoding fails + * @throws FeignException if a Feign-specific error occurs + */ @Override public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { return delegate().decode(response, type); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index f8ae900c..2ede1a4f 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -17,17 +17,56 @@ */ public class DecoratedEncoder extends DecoratedFeignComponent implements Encoder { + /** + * Constructs a {@link DecoratedEncoder} wrapping the given {@link Encoder} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedEncoder encoder = new DecoratedEncoder(
+     *     "my-client", contextFactory, clientProperties, new Encoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Encoder} to delegate to + */ public DecoratedEncoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Encoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Encoder} class from {@link FeignClientConfiguration}, + * falling back to {@link Encoder} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedEncoder.componentType();
+     * }
+ * + * @return the {@link Encoder} component type class + */ @Override protected Class componentType() { Class encoderClass = get(FeignClientConfiguration::getEncoder); return encoderClass == null ? Encoder.class : encoderClass; } + /** + * Encodes the given object into the {@link RequestTemplate} by delegating to the + * underlying {@link Encoder}. + * + *

Example Usage: + *

{@code
+     * decoratedEncoder.encode(myObject, MyObject.class, requestTemplate);
+     * }
+ * + * @param object the object to encode + * @param bodyType the body type + * @param template the {@link RequestTemplate} to encode into + * @throws EncodeException if encoding fails + */ @Override public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { delegate().encode(object, bodyType, template); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 1670573e..7a7c55d3 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -14,16 +14,53 @@ */ public class DecoratedErrorDecoder extends DecoratedFeignComponent implements ErrorDecoder { + /** + * Constructs a {@link DecoratedErrorDecoder} wrapping the given {@link ErrorDecoder} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedErrorDecoder decoder = new DecoratedErrorDecoder(
+     *     "my-client", contextFactory, clientProperties, new ErrorDecoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link ErrorDecoder} to delegate to + */ public DecoratedErrorDecoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, ErrorDecoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link ErrorDecoder} class from {@link FeignClientConfiguration}, + * falling back to {@link ErrorDecoder.Default} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedErrorDecoder.componentType();
+     * }
+ * + * @return the {@link ErrorDecoder} component type class + */ @Override protected Class componentType() { Class errorDecoderClass = get(FeignClientConfiguration::getErrorDecoder); return errorDecoderClass == null ? Default.class : errorDecoderClass; } + /** + * Decodes an error response by delegating to the underlying {@link ErrorDecoder}. + * + *

Example Usage: + *

{@code
+     * Exception ex = decoratedErrorDecoder.decode("MyClient#myMethod()", response);
+     * }
+ * + * @param methodKey the Feign method key (e.g. {@code "MyClient#myMethod()"}) + * @param response the Feign {@link Response} containing the error + * @return the decoded {@link Exception} + */ @Override public Exception decode(String methodKey, Response response) { return delegate().decode(methodKey, response); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index c5f4b376..ed1b111d 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -32,6 +32,20 @@ public abstract class DecoratedFeignComponent implements Refreshable { protected volatile T delegate; + /** + * Constructs a {@link DecoratedFeignComponent} wrapping the given delegate. + * + *

Example Usage: + *

{@code
+     * // Typically invoked via a subclass constructor
+     * super(contextId, contextFactory, clientProperties, delegate);
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original component to delegate to + */ public DecoratedFeignComponent(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { this.contextId = contextId; this.contextFactory = contextFactory; @@ -39,6 +53,17 @@ public DecoratedFeignComponent(String contextId, NamedContextFactoryExample Usage: + *
{@code
+     * T component = decoratedFeignComponent.delegate();
+     * }
+ * + * @return the current delegate instance + */ public T delegate() { T delegate = this.delegate; if (delegate == null) { @@ -49,32 +74,110 @@ public T delegate() { return delegate; } + /** + * Loads a component instance of the given type from the {@link NamedContextFactory}, + * falling back to direct instantiation if the bean is not available. + * + *

Example Usage: + *

{@code
+     * Decoder decoder = decoratedFeignComponent.loadInstanceFromContextFactory("my-client", Decoder.class);
+     * }
+ * + * @param the component type + * @param contextId the Feign client context ID + * @param componentType the class of the component to load + * @return the loaded component instance + */ @NonNull public T loadInstanceFromContextFactory(String contextId, Class componentType) { ObjectProvider beanProvider = this.contextFactory.getProvider(contextId, componentType); return beanProvider.getIfAvailable(() -> instantiateClass(componentType)); } + /** + * Returns the Feign client context ID associated with this decorated component. + * + *

Example Usage: + *

{@code
+     * String id = decoratedFeignComponent.contextId();
+     * }
+ * + * @return the context ID string + */ @NonNull public String contextId() { return this.contextId; } + /** + * Refreshes this component by clearing the delegate, causing the next call to + * {@link #delegate()} to reload the instance from the context factory. + * + *

Example Usage: + *

{@code
+     * decoratedFeignComponent.refresh();
+     * }
+ */ public void refresh() { logger.trace("the component[{}] - Refreshing delegate instance[{}] for contextId : '{}'", componentType(), this.delegate, contextId); this.delegate = null; } + /** + * Returns the Feign component type class used to resolve the delegate implementation. + * Subclasses must implement this to return the appropriate configuration class. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedFeignComponent.componentType();
+     * }
+ * + * @return the component type class + */ protected abstract Class componentType(); + /** + * Returns the default {@link FeignClientConfiguration} as defined by the + * {@link FeignClientProperties#getDefaultConfig()} key. + * + *

Example Usage: + *

{@code
+     * FeignClientConfiguration defaultConfig = decoratedFeignComponent.getDefaultConfiguration();
+     * }
+ * + * @return the default {@link FeignClientConfiguration}, or {@code null} if not present + */ public FeignClientConfiguration getDefaultConfiguration() { return this.clientProperties.getConfig().get(this.clientProperties.getDefaultConfig()); } + /** + * Returns the {@link FeignClientConfiguration} for the current Feign client context ID. + * + *

Example Usage: + *

{@code
+     * FeignClientConfiguration currentConfig = decoratedFeignComponent.getCurrentConfiguration();
+     * }
+ * + * @return the current {@link FeignClientConfiguration}, or {@code null} if not present + */ public FeignClientConfiguration getCurrentConfiguration() { return this.clientProperties.getConfig().get(contextId); } + /** + * Retrieves a value from the {@link FeignClientConfiguration} using the provided function, + * checking the default configuration first and then the current context configuration. + * + *

Example Usage: + *

{@code
+     * Class decoderClass = get(FeignClientConfiguration::getDecoder);
+     * }
+ * + * @param the value type + * @param configurationFunction the function to extract a value from the configuration + * @return the extracted value, or {@code null} if not found in either configuration + */ protected T get(Function configurationFunction) { FeignClientConfiguration config = getDefaultConfiguration(); T value = null; @@ -90,27 +193,62 @@ protected T get(Function configurationFunction) return value; } + /** + * Loads the delegate instance from the {@link NamedContextFactory} using the + * component type returned by {@link #componentType()}. + * + *

Example Usage: + *

{@code
+     * T instance = decoratedFeignComponent.loadInstance();
+     * }
+ * + * @return the loaded delegate instance + */ protected T loadInstance() { Class componentType = componentType(); String contextId = contextId(); return loadInstanceFromContextFactory(contextId, componentType); } + /** {@inheritDoc} */ @Override public int hashCode() { return delegate().hashCode(); } + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { return delegate().equals(obj); } + /** {@inheritDoc} */ @Override public String toString() { return delegate().toString(); } + /** + * Factory method to instantiate a {@link DecoratedFeignComponent} subclass by locating + * the appropriate constructor via reflection. + * + *

Example Usage: + *

{@code
+     * DecoratedContract contract = DecoratedFeignComponent.instantiate(
+     *     DecoratedContract.class, Contract.class,
+     *     "my-client", contextFactory, clientProperties, originalContract);
+     * }
+ * + * @param the decorated component type + * @param the Feign component type + * @param decoratedClass the {@link DecoratedFeignComponent} subclass to instantiate + * @param componentClass the Feign component interface class + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for context resolution + * @param clientProperties the {@link FeignClientProperties} for configuration + * @param delegate the original delegate instance + * @return a new instance of the decorated component + */ public static , T> W instantiate(Class decoratedClass, Class componentClass, String contextId, diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index fcfddc41..37eb3d1a 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -25,10 +25,35 @@ public class DecoratedQueryMapEncoder extends DecoratedFeignComponentExample Usage: + *
{@code
+     * DecoratedQueryMapEncoder encoder = new DecoratedQueryMapEncoder(
+     *     "my-client", contextFactory, clientProperties, new QueryMapEncoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link QueryMapEncoder} to delegate to + */ public DecoratedQueryMapEncoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, QueryMapEncoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link QueryMapEncoder} class from {@link FeignClientConfiguration}, + * falling back to {@link PageableSpringQueryMapEncoder} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedQueryMapEncoder.componentType();
+     * }
+ * + * @return the {@link QueryMapEncoder} component type class + */ @Override protected Class componentType() { Class queryMapEncoderClass = getQueryMapEncoder(getCurrentConfiguration()); @@ -42,6 +67,20 @@ private Class getQueryMapEncoder(FeignClientConfiguration feign return getQueryMapEncoder(getQueryMapEncoderMethodHandle, feignClientConfiguration); } + /** + * Retrieves the {@link QueryMapEncoder} class from a {@link FeignClientConfiguration} + * using a {@link MethodHandle} for compatibility across Spring Cloud versions. + * + *

Example Usage: + *

{@code
+     * Class encoderClass = DecoratedQueryMapEncoder.getQueryMapEncoder(
+     *     getQueryMapEncoderMethodHandle, feignClientConfiguration);
+     * }
+ * + * @param methodHandle the {@link MethodHandle} to invoke {@code getQueryMapEncoder} + * @param feignClientConfiguration the configuration to read from + * @return the configured {@link QueryMapEncoder} class, or {@code null} if unavailable + */ static Class getQueryMapEncoder(MethodHandle methodHandle, FeignClientConfiguration feignClientConfiguration) { if (methodHandle == NOT_FOUND_METHOD_HANDLE) { return null; @@ -55,6 +94,18 @@ static Class getQueryMapEncoder(MethodHandle methodHandle, Feig return queryMapEncoderClass; } + /** + * Encodes the given object into a query parameter map by delegating to the + * underlying {@link QueryMapEncoder}. + * + *

Example Usage: + *

{@code
+     * Map queryParams = decoratedQueryMapEncoder.encode(myQueryObject);
+     * }
+ * + * @param object the object to encode as query parameters + * @return a map of query parameter names to values + */ @Override public Map encode(Object object) { return delegate().encode(object); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java index f4dc74fe..ae028a36 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java @@ -14,27 +14,88 @@ */ public class DecoratedRetryer extends DecoratedFeignComponent implements Retryer { + /** + * Constructs a {@link DecoratedRetryer} wrapping the given {@link Retryer} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedRetryer retryer = new DecoratedRetryer(
+     *     "my-client", contextFactory, clientProperties, new Retryer.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Retryer} to delegate to + */ public DecoratedRetryer(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Retryer delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Retryer} class from {@link FeignClientConfiguration}, + * falling back to {@link Retryer} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedRetryer.componentType();
+     * }
+ * + * @return the {@link Retryer} component type class + */ @Override protected Class componentType() { Class retryerClass = get(FeignClientConfiguration::getRetryer); return retryerClass == null ? Retryer.class : retryerClass; } + /** + * Continues or propagates the retry by delegating to the underlying {@link Retryer}. + * + *

Example Usage: + *

{@code
+     * try {
+     *     // Feign call
+     * } catch (RetryableException e) {
+     *     decoratedRetryer.continueOrPropagate(e);
+     * }
+     * }
+ * + * @param e the {@link RetryableException} to evaluate for retry + */ @Override public void continueOrPropagate(RetryableException e) { continueOrPropagate(delegate(), e); } + /** + * Delegates the continue-or-propagate decision to the given {@link Retryer} if it + * is not {@code null}. + * + *

Example Usage: + *

{@code
+     * DecoratedRetryer.continueOrPropagate(retryerInstance, retryableException);
+     * }
+ * + * @param retryer the {@link Retryer} to delegate to, may be {@code null} + * @param e the {@link RetryableException} to evaluate + */ static void continueOrPropagate(Retryer retryer, RetryableException e) { if (retryer != null) { retryer.continueOrPropagate(e); } } + /** + * Returns a clone of the delegate {@link Retryer}. + * + *

Example Usage: + *

{@code
+     * Retryer cloned = decoratedRetryer.clone();
+     * }
+ * + * @return a cloned {@link Retryer} instance + */ @Override public Retryer clone() { return delegate().clone(); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java index 071c5395..42504bd5 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java @@ -10,8 +10,29 @@ */ public class NoOpRequestInterceptor implements RequestInterceptor { + /** + * A no-operation {@link RequestInterceptor} that does nothing when applied. + * Used as a placeholder to ensure at least one interceptor is registered. + * + *

Example Usage: + *

{@code
+     * RequestInterceptor noOp = NoOpRequestInterceptor.INSTANCE;
+     * noOp.apply(requestTemplate); // does nothing
+     * }
+ */ public static final NoOpRequestInterceptor INSTANCE = new NoOpRequestInterceptor(); + /** + * Applies this interceptor to the given {@link RequestTemplate}. This implementation + * intentionally performs no operation. + * + *

Example Usage: + *

{@code
+     * NoOpRequestInterceptor.INSTANCE.apply(requestTemplate);
+     * }
+ * + * @param template the {@link RequestTemplate} (ignored) + */ @Override public void apply(RequestTemplate template) { //no op From 6453215cbb8860afdd4c934a7ac240cedd63ef05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:13:56 +0000 Subject: [PATCH 287/310] Add JavaDoc to RegistrationEvent constructor and ServiceInstanceUtils.attachMetadata Co-authored-by: mercyblitz <533114+mercyblitz@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsphere-projects/microsphere-spring-cloud/sessions/30c49647-70b2-4c4a-9853-02725377f52f --- .../registry/event/RegistrationEvent.java | 14 ++++++++++++++ .../service/util/ServiceInstanceUtils.java | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java index 84d9aa3a..939afba7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java @@ -42,6 +42,20 @@ public abstract class RegistrationEvent extends ApplicationEvent { private final ServiceRegistry registry; + /** + * Create a new {@link RegistrationEvent} with the given {@link ServiceRegistry} and {@link Registration}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * // Typically used through a subclass such as RegistrationRegisteredEvent
+     * RegistrationRegisteredEvent event = new RegistrationRegisteredEvent(registry, registration);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that triggered the event + * @param source the {@link Registration} associated with the event + */ public RegistrationEvent(ServiceRegistry registry, Registration source) { super(source); Assert.notNull(registry, "The 'registry' must not be null"); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 71aab2bf..f92103fa 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -65,6 +65,22 @@ public class ServiceInstanceUtils extends BaseUtils { private static final Logger logger = getLogger(ServiceInstanceUtils.class); + /** + * Attach {@link WebEndpointMapping} metadata to the given {@link ServiceInstance}. + * The web endpoint mappings are serialized as JSON and stored in the service instance's + * metadata under the {@link io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants#WEB_MAPPINGS_METADATA_NAME} key. + * + *

Example Usage: + *

{@code
+     * ServiceInstance serviceInstance = new DefaultServiceInstance("id", "service", "localhost", 8080, false);
+     * Collection mappings = new ArrayList<>();
+     * ServiceInstanceUtils.attachMetadata("/context", serviceInstance, mappings);
+     * }
+ * + * @param contextPath the web application context path + * @param serviceInstance the {@link ServiceInstance} to attach metadata to + * @param webEndpointMappings the collection of {@link WebEndpointMapping}s to attach + */ public static void attachMetadata(String contextPath, ServiceInstance serviceInstance, Collection webEndpointMappings) { Map metadata = serviceInstance.getMetadata(); StringJoiner jsonBuilder = new StringJoiner(COMMA + LINE_SEPARATOR, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET); From 556728334a7e042899a9b7d601b0fb18799437af Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Mar 2026 15:37:16 +0800 Subject: [PATCH 288/310] Add Dependabot config for Maven updates Add .github/dependabot.yml to enable daily Dependabot version updates for the Maven ecosystem at the repository root. Limits open pull requests to 10 and includes documentation comments for configuration. --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..3fe4d065 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "maven" # See documentation for possible values + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 \ No newline at end of file From e96d6d2b0ec5b7538d57e9f65c7001a82d6fb6b3 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Sun, 22 Mar 2026 15:37:57 +0800 Subject: [PATCH 289/310] Add workflow to auto-merge main into branches Introduce a GitHub Actions workflow that automatically merges changes from main into dev and release when main receives a push. The workflow runs two jobs (merge-to-dev and merge-to-release), checks out the repo, attempts a --no-ff merge of origin/main with a chore commit message containing [skip ci], and pushes the target branch. If the target branch is missing or a merge conflict occurs, the job errors and exits so manual intervention is required. A concurrency group prevents overlapping runs. --- .github/workflows/merge-main-to-branches.yml | 66 ++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/merge-main-to-branches.yml diff --git a/.github/workflows/merge-main-to-branches.yml b/.github/workflows/merge-main-to-branches.yml new file mode 100644 index 00000000..f4715238 --- /dev/null +++ b/.github/workflows/merge-main-to-branches.yml @@ -0,0 +1,66 @@ +# This workflow automatically merges the 'main' branch into 'dev' and 'release' branches +# whenever changes are pushed to 'main', without requiring manual confirmation. + +name: Merge Main to Dev and Release + +on: + push: + branches: + - main + +concurrency: + group: merge-main-to-branches + cancel-in-progress: false + +jobs: + merge-to-dev: + name: Merge main → dev + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Merge main into dev + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + if ! git checkout dev; then + echo "::error::Branch 'dev' does not exist. Skipping merge." + exit 1 + fi + if ! git merge --no-ff origin/main -m "chore: merge main into dev [skip ci]"; then + echo "::error::Merge conflict detected when merging main into dev. Manual intervention required." + exit 1 + fi + git push origin dev + + merge-to-release: + name: Merge main → release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Merge main into release + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + if ! git checkout release; then + echo "::error::Branch 'release' does not exist. Skipping merge." + exit 1 + fi + if ! git merge --no-ff origin/main -m "chore: merge main into release [skip ci]"; then + echo "::error::Merge conflict detected when merging main into release. Manual intervention required." + exit 1 + fi + git push origin release From 692616f7849a17f0e73dcea042e136e3506eb30b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 Mar 2026 08:02:59 +0000 Subject: [PATCH 290/310] Bump org.testcontainers:testcontainers-bom from 2.0.1 to 2.0.4 Bumps [org.testcontainers:testcontainers-bom](https://github.com/testcontainers/testcontainers-java) from 2.0.1 to 2.0.4. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/2.0.1...2.0.4) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers-bom dependency-version: 2.0.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- microsphere-spring-cloud-parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 84b562e5..13b87d17 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -21,7 +21,7 @@ 0.2.5 - 2.0.1 + 2.0.4 From b5935579bdcb4afcb44c3401ee53b843c652b6c8 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Mar 2026 12:33:43 +0800 Subject: [PATCH 291/310] Use Maven Wrapper in CI workflow Update GitHub Actions workflow to invoke ./mvnw instead of system mvn to ensure a consistent Maven version on runners. Removed the maven cache setting and adjusted the run step to call the wrapper with the existing flags (--batch-mode, --update-snapshots, --file pom.xml) for more reproducible builds. --- .github/workflows/maven-build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 27eeb427..451ce020 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -35,10 +35,9 @@ jobs: with: distribution: 'temurin' java-version: ${{ matrix.java }} - cache: maven - name: Build with Maven - run: mvn + run: ./mvnw --batch-mode --update-snapshots --file pom.xml From 30df6132dad67f566a10b8f33797797e41bf6168 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Mon, 23 Mar 2026 12:36:08 +0800 Subject: [PATCH 292/310] Bump GH Actions to v5 and add release job Upgrade CI actions and add automated release flow. Updated actions/checkout and actions/setup-java from v4 to v5 in both maven-build.yml and maven-publish.yml. In maven-publish.yml changed the default revision placeholder, added a validation step to ensure inputs.revision matches major.minor.patch, and introduced a release job that checks out full history, creates a git tag, creates a GitHub release (using gh), increments the patch version in pom.xml to the next -SNAPSHOT, and commits/pushes the bumped version. These changes modernize the workflows and automate tagging, release creation, and post-release version bumping. --- .github/workflows/maven-build.yml | 4 +- .github/workflows/maven-publish.yml | 68 +++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 451ce020..da2590ed 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -23,7 +23,7 @@ jobs: maven-profile-spring-cloud: [ 'spring-cloud-2022' , 'spring-cloud-2023' , 'spring-cloud-2024' , 'spring-cloud-2025' ] steps: - name: Checkout Source - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Testcontainers Cloud Client uses: atomicjar/testcontainers-cloud-setup-action@v1 @@ -31,7 +31,7 @@ jobs: token: ${{ secrets.TC_CLOUD_TOKEN }} - name: Setup JDK ${{ matrix.Java }} - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: ${{ matrix.java }} diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 6b421c08..7b1b44ed 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -16,18 +16,25 @@ on: revision: description: 'The version to publish for Spring Cloud 2022+ and JDK 17+' required: true - default: '2.0.0-SNAPSHOT' + default: '${major}.${minor}.${patch}' jobs: build: runs-on: ubuntu-latest if: ${{ inputs.revision }} steps: + - name: Validate version format + run: | + if ! echo "${{ inputs.revision }}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "Error: version '${{ inputs.revision }}' does not match the required pattern major.minor.patch" + exit 1 + fi + - name: Checkout Source - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Maven Central Repository - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -51,3 +58,58 @@ jobs: SIGN_KEY_ID: ${{ secrets.OSS_SIGNING_KEY_ID_LONG }} SIGN_KEY: ${{ secrets.OSS_SIGNING_KEY }} SIGN_KEY_PASS: ${{ secrets.OSS_SIGNING_PASSWORD }} + + + release: + runs-on: ubuntu-latest + needs: build + permissions: + contents: write + steps: + - name: Checkout Source + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Create Tag + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + if git rev-parse "${{ inputs.revision }}" >/dev/null 2>&1; then + echo "Tag ${{ inputs.revision }} already exists, skipping." + else + git tag ${{ inputs.revision }} + git push origin ${{ inputs.revision }} + fi + + - name: Create Release + run: | + if gh release view "v${{ inputs.revision }}" >/dev/null 2>&1; then + echo "Release v${{ inputs.revision }} already exists, skipping." + else + gh release create v${{ inputs.revision }} \ + --title "v${{ inputs.revision }}" \ + --generate-notes \ + --latest + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Increment patch version + run: | + CURRENT="${{ inputs.revision }}" + MAJOR=$(echo "$CURRENT" | cut -d. -f1) + MINOR=$(echo "$CURRENT" | cut -d. -f2) + PATCH=$(echo "$CURRENT" | cut -d. -f3) + NEXT_PATCH=$((PATCH + 1)) + NEXT_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}-SNAPSHOT" + sed -i "s|^\( *\)[^<]*|\1${NEXT_VERSION}|" pom.xml + echo "Bumped version from ${CURRENT} to ${NEXT_VERSION}" + + - name: Commit and push next version + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add pom.xml + git diff --cached --quiet && echo "No changes to commit" || \ + git commit -m "chore: bump version to next patch after publishing ${{ inputs.revision }}" && git push \ No newline at end of file From 4966340d2eef89cdb536b4ecbb0684134b536fb6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:49:25 +0000 Subject: [PATCH 293/310] refactor: wrap unguarded logger calls in isXxxEnabled() guards Co-authored-by: mercyblitz <533114+mercyblitz@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsphere-projects/microsphere-spring-cloud/sessions/e48e837d-515a-41bc-82ca-62cba0ba78a9 --- .../WebServiceRegistryAutoConfiguration.java | 4 +++- .../endpoint/ServiceDeregistrationEndpoint.java | 8 ++++++-- .../endpoint/ServiceRegistrationEndpoint.java | 8 ++++++-- .../client/service/util/ServiceInstanceUtils.java | 10 +++++++--- .../event/TomcatDynamicConfigurationListener.java | 12 +++++++++--- .../FeignClientSpecificationPostProcessor.java | 6 ++++-- .../components/DecoratedFeignComponent.java | 8 ++++++-- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java index 02f61c23..fe81ee6e 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java @@ -95,7 +95,9 @@ private void excludeMappings(Set mappings) { WebEndpointMapping mapping = iterator.next(); String[] patterns = mapping.getPatterns(); if (isExcludedMapping(mapping, patterns) || isActuatorWebEndpointMapping(mapping, patterns)) { - logger.trace("The '{}' was excluded", mapping); + if (logger.isTraceEnabled()) { + logger.trace("The '{}' was excluded", mapping); + } iterator.remove(); } } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java index e9858f1d..84b0e481 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java @@ -33,10 +33,14 @@ public boolean stop() { boolean isRunning = isRunning(); if (isRunning) { serviceRegistry.deregister(registration); - logger.info("Service[name : '{}'] is deregistered!", applicationName); + if (logger.isInfoEnabled()) { + logger.info("Service[name : '{}'] is deregistered!", applicationName); + } setRunning(false); } else { - logger.warn("Service[name : '{}'] is not registered, deregistration can't be executed!", applicationName); + if (logger.isWarnEnabled()) { + logger.warn("Service[name : '{}'] is not registered, deregistration can't be executed!", applicationName); + } } return isRunning; } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java index afc898ad..bd6e78ef 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java @@ -72,9 +72,13 @@ public boolean start() { if (!isRunning) { serviceRegistry.register(registration); setRunning(true); - logger.info("Service[name : '{}'] is registered!", applicationName); + if (logger.isInfoEnabled()) { + logger.info("Service[name : '{}'] is registered!", applicationName); + } } else { - logger.warn("Service[name : '{}'] was registered!", applicationName); + if (logger.isWarnEnabled()) { + logger.warn("Service[name : '{}'] was registered!", applicationName); + } } return isRunning; } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index f92103fa..1dae14d9 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -86,14 +86,18 @@ public static void attachMetadata(String contextPath, ServiceInstance serviceIns StringJoiner jsonBuilder = new StringJoiner(COMMA + LINE_SEPARATOR, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET); webEndpointMappings.stream().map(WebEndpointMapping::toJSON).forEach(jsonBuilder::add); String json = jsonBuilder.toString(); - logger.trace("Web Endpoint Mappings JSON: \n{}", json); + if (logger.isTraceEnabled()) { + logger.trace("Web Endpoint Mappings JSON: \n{}", json); + } json = json.replace(LINE_SEPARATOR, EMPTY_STRING); String encodedJson = encode(json); metadata.put(WEB_CONTEXT_PATH_METADATA_NAME, contextPath); metadata.put(WEB_MAPPINGS_METADATA_NAME, encodedJson); - logger.trace("ServiceInstance's metadata :"); - metadata.forEach((name, value) -> logger.trace("{} : {}", name, value)); + if (logger.isTraceEnabled()) { + logger.trace("ServiceInstance's metadata :"); + metadata.forEach((name, value) -> logger.trace("{} : {}", name, value)); + } } /** diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java index 42abdb41..500e3f82 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java @@ -119,13 +119,17 @@ private void initCurrentServerProperties() { @Override public void onApplicationEvent(EnvironmentChangeEvent event) { if (!isSourceFrom(event)) { - logger.trace("Current context[id : '{}'] receives the other changed property names : {}", context.getId(), event.getKeys()); + if (logger.isTraceEnabled()) { + logger.trace("Current context[id : '{}'] receives the other changed property names : {}", context.getId(), event.getKeys()); + } return; } Set serverPropertyNames = filterServerPropertyNames(event); if (serverPropertyNames.isEmpty()) { - logger.trace("Current context[id : '{}'] does not receive the property change of ServerProperties, keys : {}", context.getId(), event.getKeys()); + if (logger.isTraceEnabled()) { + logger.trace("Current context[id : '{}'] does not receive the property change of ServerProperties, keys : {}", context.getId(), event.getKeys()); + } return; } @@ -151,7 +155,9 @@ private boolean isServerPropertyName(String propertyName) { private void configureTomcatIfChanged(Set serverPropertyNames) { ServerProperties refreshableServerProperties = getRefreshableServerProperties(serverPropertyNames); - logger.trace("The ServerProperties property is changed to: {}", getProperties(environment, serverPropertyNames)); + if (logger.isTraceEnabled()) { + logger.trace("The ServerProperties property is changed to: {}", getProperties(environment, serverPropertyNames)); + } configureConnector(refreshableServerProperties); // Reset current ServerProperties initCurrentServerProperties(); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java index 895c3bc2..4eb359bf 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java @@ -55,7 +55,9 @@ private void injectAutoRefreshCapability(FeignClientSpecification specification) Class[] originConfigurationClasses = specification.getConfiguration(); Class[] newConfigurationClasses = combine(AUTO_REFRESH_CAPABILITY_CLASS, originConfigurationClasses); specification.setConfiguration(newConfigurationClasses); - logger.trace("The Configuration classes: before - {} , after - {}", arrayToString(originConfigurationClasses), - arrayToString(newConfigurationClasses)); + if (logger.isTraceEnabled()) { + logger.trace("The Configuration classes: before - {} , after - {}", arrayToString(originConfigurationClasses), + arrayToString(newConfigurationClasses)); + } } } \ No newline at end of file diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index ed1b111d..7c32bc92 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -68,7 +68,9 @@ public T delegate() { T delegate = this.delegate; if (delegate == null) { delegate = loadInstance(); - logger.trace("the component[{}] - Creating delegate instance[{}] for contextId: '{}'", componentType(), delegate, contextId); + if (logger.isTraceEnabled()) { + logger.trace("the component[{}] - Creating delegate instance[{}] for contextId: '{}'", componentType(), delegate, contextId); + } this.delegate = delegate; } return delegate; @@ -119,7 +121,9 @@ public String contextId() { * } */ public void refresh() { - logger.trace("the component[{}] - Refreshing delegate instance[{}] for contextId : '{}'", componentType(), this.delegate, contextId); + if (logger.isTraceEnabled()) { + logger.trace("the component[{}] - Refreshing delegate instance[{}] for contextId : '{}'", componentType(), this.delegate, contextId); + } this.delegate = null; } From 64f7dacb221c3655e4aaee74ca94edca1982b4ed Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Mar 2026 20:53:49 +0800 Subject: [PATCH 294/310] Bump parent pom version to 0.2.5 Update parent io.github.microsphere-projects:microsphere-build version from 0.2.2 to 0.2.5 in pom.xml to pick up recent build fixes and updates. No other changes in this commit. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 011b36a2..ef2394b3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.microsphere-projects microsphere-build - 0.2.2 + 0.2.5 4.0.0 From 39ca7e0e278f0c7536bfb27b632ed93b63f79a71 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Mar 2026 20:54:26 +0800 Subject: [PATCH 295/310] Bump dependency versions; add 2025.1 profile Update dependency BOM versions and add a new Spring Cloud profile. Changes: - Bump microsphere-spring-boot.version 0.2.5 -> 0.2.6. - Update spring-cloud-alibaba.version in 2023 profile 2023.0.3.2 -> 2023.0.3.4. - spring-cloud-2024 profile: spring-boot 3.4.7 -> 3.4.12, spring-cloud 2024.0.2 -> 2024.0.3, spring-cloud-alibaba 2023.0.3.2 -> 2023.0.3.4. - spring-cloud-2025 profile: spring-boot 3.5.0 -> 3.5.8, spring-cloud 2025.0.0 -> 2025.0.1, spring-cloud-alibaba 2023.0.3.2 -> 2025.0.0.0. - Add new profile spring-cloud-2025.1 with spring-boot 4.0.2, spring-cloud 2025.1.1, and spring-cloud-alibaba 2025.1.0.0. These updates align BOM versions with newer Spring Boot / Spring Cloud releases and introduce a profile for the 2025.1 line. --- microsphere-spring-cloud-parent/pom.xml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml index 13b87d17..41a4be0e 100644 --- a/microsphere-spring-cloud-parent/pom.xml +++ b/microsphere-spring-cloud-parent/pom.xml @@ -20,7 +20,7 @@ - 0.2.5 + 0.2.6 2.0.4 @@ -92,16 +92,16 @@ 3.3.13 2023.0.6 - 2023.0.3.2 + 2023.0.3.4
spring-cloud-2024 - 3.4.7 - 2024.0.2 - 2023.0.3.2 + 3.4.12 + 2024.0.3 + 2023.0.3.4 @@ -111,9 +111,18 @@ true - 3.5.0 - 2025.0.0 - 2023.0.3.2 + 3.5.8 + 2025.0.1 + 2025.0.0.0 + + + + + spring-cloud-2025.1 + + 4.0.2 + 2025.1.1 + 2025.1.0.0 From 4b9765c77bb7e5e519934771514bd324d2bc5eba Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Mar 2026 21:01:31 +0800 Subject: [PATCH 296/310] Trigger Maven workflow on main and dev pushes Update CI workflow to run on pushes to both `main` and `dev` branches (was only `dev`). This ensures the Maven build runs for direct pushes to main as well; pull_request triggers already cover `main`, `dev`, and `release`. Changed .github/workflows/maven-build.yml accordingly. --- .github/workflows/maven-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index da2590ed..366b2dc0 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -10,7 +10,7 @@ name: Maven Build on: push: - branches: [ 'dev' ] + branches: [ 'main', 'dev' ] pull_request: branches: [ 'main', 'dev' , 'release' ] From 7d8160c31a08fbfd51e1913743de0f7fefe09ac7 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Mar 2026 21:01:40 +0800 Subject: [PATCH 297/310] Migrate Maven wrapper to script-only (3.3.4) Remove the committed maven-wrapper.jar and switch the Maven Wrapper to script-only form (wrapperVersion=3.3.4). Update .mvn/wrapper/maven-wrapper.properties to use distributionType=only-script and point distributionUrl to an aliyun mirror for apache-maven-3.9.9. Add platform wrappers mvnw (unix, executable) and mvnw.cmd (Windows) to download and install the Maven distribution at runtime. This reduces committed binary artifacts and updates the wrapper and Maven versions. --- .mvn/wrapper/maven-wrapper.jar | Bin 59925 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 19 +- mvnw | 295 ++++++++++++++++++++++++++ mvnw.cmd | 189 +++++++++++++++++ 4 files changed, 487 insertions(+), 16 deletions(-) delete mode 100755 .mvn/wrapper/maven-wrapper.jar create mode 100755 mvnw create mode 100644 mvnw.cmd diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100755 index bf82ff01c6cdae4a1bb754a6e062954d77ac5c11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59925 zcmb5U1CS=sk~ZA7ZQHhc+Mc%Ywrx+_*0gQgw(Xv_ZBOg(y}RG;-uU;sUu;#Jh>EHw zGfrmZsXF;&D$0O@!2kh40RbILm8t;!w*&h7T24$wm|jX=oKf)`hV~7E`UmXw?e4Pt z`>_l#5YYGC|ANU0%S(xiDXTEZiATrw!Spl1gyQYxsqjrZO`%3Yq?k$Dr=tVr?HIeHlsmnE9=ZU6I2QoCjlLn85rrn7M!RO}+ z%|6^Q>sv`K3j6Ux>as6NoB}L8q#ghm_b)r{V+Pf3xj>b^+M8ZFY`k|FHgl zM!^0D!qDCjU~cj+fXM$0v@vuwvHcft?EeYw=4fbdZ{qkb#PI)>7{J=%Ux*@pi~i^9 z{(nu6>i-Y^_7lUudx7B}(hUFa*>e0ZwEROS{eRc_U*VV`F$C=Jtqb-$9MS)~&L3im zV)8%4)^9W3c4IT94|h)3k zdAT_~?$Z0{&MK=M0K)Y#_0R;gEjTs0uy4JHvr6q{RKur)D^%t>W+U;a*TZ;VL{kcnJJT z3mD=m7($$%?Y#>-Edcet`uWDH(@wIl+|_f#5l8odHg_|+)4AAYP9)~B^10nU306iE zaS4Y#5&gTL4eHH6&zd(VGyR0Qccx;>0R~Y5#29OkJpSAyr4&h1CYY|I}o)z ze}OiPf5V~(ABejc1pN%8rJQHwPn_`O*q7Dm)p}3K(mm1({hFmfY{yYbM)&Y`2R=h? zTtYwx?$W-*1LqsUrUY&~BwJjr)rO{qI$a`=(6Uplsti7Su#&_03es*Yp0{U{(nQCr z?5M{cLyHT_XALxWu5fU>DPVo99l3FAB<3mtIS<_+71o0jR1A8rd30@j;B75Z!uH;< z{shmnFK@pl080=?j0O8KnkE;zsuxzZx z4X2?!Dk7}SxCereOJK4-FkOq3i{GD#xtAE(tzLUiN~R2WN*RMuA3uYv-3vr9N8;p- z0ovH_gnvKnB5M{_^d`mUsVPvYv`38c2_qP$*@)N(ZmZosbxiRG=Cbm`0ZOx23Zzgs zLJPF;&V~ZV;Nb8ELEf73;P5ciI7|wZBtDl}on%WwtCh8Lf$Yfq`;Hb1D!-KYz&Kd< z+WE+o-gPb6S%ah2^mF80rK=H*+8mQdyrR+)Ar5krl4S!TAAG+sv8o+Teg)`9b22%4 zI7vnPTq&h=o=Z|$;>tEj(i@KN^8N@nk}}6SBhDIGCE4TrmVvM^PlBVZsbZcmR$P7v3{Pw88(jhhI?28MZ>uB%H z&+HAqu-MDFVk5|LYqUXBMR74n1nJ|qLNe#G7UaE>J{uX(rz6McAWj)Ui2R!4y&B01 z`}LOF7k|z0$I+psk+U^Z3YiAH-{>k*@z|0?L4MPNdtsPB+(F791LsRX$Dm(Gycm1k}n z#a2T#*)k-v{}p@^L5PC^@bH+-YO4v`l7Gq)9pgSns??ISG!M6>7&GySTZkVhykqk* zijh9sE`ky?DQPo+7}Vu@?}15_zTovL$r%h~*)=6*vTz?G#h|~>p(ukh%MKOCV^Jxa zi~lMP5+^-OW%Te@b#UoL6T1%9h-W}*hUtdu!>odxuT`kTg6U3+a@6QTiwM0I zqXcEI2x-gOS74?=&<18fYRv&Ms)R>e;Qz&0N20K9%CM_Iq#3V8%pwU>rAGbaXoGVS z-r5a$;fZ>75!`u@7=vV?y@7J;S;E#lvQ?Ar>%ao zOX)rc794W?X64tUEk>y|m_aCxU#N>o!Xw7##(7dIZDuYn0+9DoafcrK_(IUSl$m`A zZF1;0D&2KMWxq{!JlB#Yo*~RCRR~RBkfBb1)-;J`)fjK%LQgUfj-6(iNb3|)(r4fB z-3-I@OH8NV#Rr1`+c=9-0s3A3&EDUg1gC3 zVVb)^B@WE;ePBj#Rg2m!twC+Fe#io0Tzv)b#xh64;e}usgfxu(SfDvcONCs$<@#J@ zQrOhaWLG+)32UCO&4%us+o5#=hq*l-RUMAc6kp~sY%|01#<|RDV=-c0(~U2iF;^~Z zEGyIGa;#2iBbNLww#a{)mO^_H26>4DzS zW3Ln9#3bY?&5y|}CNM1c33!u1X@E`O+UCM*7`0CQ9bK1=r%PTO%S(Xhn0jV&cY5!; zknWK#W@!pMK$6<7w)+&nQZwlnxpxV_loGvL47cDabBUjf{BtT=5h1f2O&`n<$C%+3 zm$_pHm|BCm`G@w&Db)?4fM_YHa%}k|QMMl^&R}^}qj!z-hSy7npCB+A1jrr|1}lLs zw#c+UwVNwxP{=c;rL2BGdx*7zEe1Bcd{@%1-n8y7D4tiWqfpUVh-lHmLXM^KZShOH z*xFp)8|Y+bM`|>mg}p~MOHeh4Ev0_oE?T1n|HMCuuhyf*JDmFP(@8+hi#f-8(!7>g zH}lOHg#Nw(x(LkB`Q;g)oVAM{fXLqlew~t2GU);6V}=6Hx<4O5T!!-c93s;NqxUDm zofsXe!Q%wAD~BBUQ3dIiCtR4WMh-t>ISH?ZMus*wja+&<^&&Gm-nBlDvNS4vFnsl^ ztNpIbyMcWMPfKMe=YnWeIVj|?e>nZbwm$=sV@Qj@A@PE#Gnjlk{CGPDsqFS_)9LEa zuKx7=Sa>|^MiSKB?)pG()OoM}_%lx|mMlX&!?+`^^4bT=yz=ZoxWH_ngA*jX*IZcHOjb62dT(qTvBPn`2AFuL0q` zG+T@693;<++Z2>R2bD`qi0y2-Zf>Ao)K0f&d2P zfP78gpA6dVzjNaH?(M_mDL)R0U=lEaBZvDI4%DXB?8uw7yMJ~gE#%4F`v`Nr+^}vY zNk!D`{o4;L#H`(&_&69MXgCe`BzoU+!tF?72v9Ywy}vJ>QpqhIh5d@V>0xHtnyvuH zkllrfsI^;%I{@6lUi{~rA_w0mAm940-d++CcVAe<%1_RMLrby@&kK~cJQDXKIiybT z-kqt-K3rNz|3HT@un%{nW0OI{_DTXa-Gt@ONBB`7yPzA#K+GBJn@t@$=}KtxV871R zdlK|BI%we#j)k%=s3KJX%`+e4L~_qWz2@P z#)_IbEn(N_Ea!@g!rjt?kw;wph2ziGM|CPAOSzd(_Cp~tpAPO_7R!r5msJ4J@6?@W zb7r0)y);{W17k3}ls4DaNKdRpv@#b#oh4zlV3U@E2TCET9y3LQs1&)-c6+olCeAYp zOdn^BGxjbJIUL0yuFK_Dqpq%@KGOvu(ZgtKw;O*bxSb1Yp#>D?c~ir9P;<3wS2!-P zMc%jlfyqGiZiTjBA(FcUQ9mq#D-cvB9?$ctRZ;8+0s}_I8~6!fM~(jD=psem4Ee>J zWw&CJ7z{P9{Q7Ubye9)gwd`}~OSe#Rf$+;U1GvliVlhuHCK9yJZ2>_y@94OzD`#Ze z9)jO->@7)Bx~CeDJqQK|0%Pfmg&-w7mHdq3hENhQ;IKK;+>|iFp;c?M^kE!kGY&!y zk0I0Fk*!r6F59pwb<6v2ioT*86d(Tee%E1tmlfVjA#rHqA%a~cH`ct#9wX$-o9erW zXJEEOOJ&dezJO$TrCEB2LVOPr4a1H9%k<&lGZo1LDHNDa_xlUqto!CGM^Y}cxJn@x ziOYwn=mHBj_FAw|vMAK^Oqb(dg4Q?7Umqwc#pL?^vpIVNpINMEiP4Ml+xGo3f$#n$ zSTA3aJ)pM~4OPF>OOXOH&EW^(@T%5hknDw^bLpH%?4DjNr1s9Q9(3+8zy87a{1<&7 zQ@0A|_nnege~*7+LF5%wzLWD`lXWotLU4Y&{0i|(kn5hdwj^9o@)((-j86#TKNN|Got?9j^EYE8XJ}!o>}=@hY~siOur_pZ`mJW+ zg}Q?7Q_~bhh6s%uqEU!cv`B=jEp1K|eld>}I`pHtYzif`aZCe88}u$J6??5!TjY7Z zi_PXV!PdeegMrv48ein(j_-BWXDa73W&U|uQY2%u#HZ5hI@4>q?YPsd?K$Vm;~XD| za8S@laz_>}&|R%BD&V-i4%Q6dPCyvF3vd@kU>rvB!x*5ubENu_D>JSGcAwBe1xXs> z#6>7f9RU7nBW^%VMe9x%V$+)28`I~HD=gM$1Sivq)mNV>xD~CileqbUCO{vWg4Rh# zor2~~5hCEN)_0u$!q<(|hY5H=>Bbu%&{4ZV_rD1<#JLjo7b^d16tZ8WIRSY-f>X{Z zrJFo^lCo+3AagC{EW4g= z#o?8?8vCfRVy)U15jF^~4Gl{&Ybt92qe)hZ^_X>`+9vgWKwyZiaxznCo|TfVh3jIi zcEf?H`U;iFaJh=3Gy2JXApN`o zE=O1Gg$YQt6|76IiMNF?q#SA1bPB@dw#H+-V@9gL>;1mg+Cb#k1ey8`dvR+(4ebj= zUV1Z)tKRo}YEh@TN=$v(;aR{{n8vk`w|nNuHuckt$h27 z8*aBefUxw1*r#xB#9egcpXEi_*UAJYXXk!L7j@ zEHre9TeA?cA^qC?JqR^Tr%MObx)3(nztwV-kCeU-pv~$-T<>1;$_fqD%D@B13@6nJvk$Tb z%oMcxY|wp&wv8pf7?>V>*_$XB&mflZG#J;cO4(H9<>)V(X0~FRrD50GSAr_n^}6UI=}MTD3{q9rAHBj;!)G9GGx;~wMc8S8e@_! z_A@g2tE?_kGw#r}Y07^+v*DjB7v08O#kihqtSjT)2uwHG1UbSIKEAO<7Nt3T;R`YCSSj z!e)qa4Y~g>{F>ed`oWGW>((#s$zQGbsS&sg}^pBd?yeAN05Roe8> zT5^XsnI??pY-edI9fQNz3&cr}&YORzr4;sw1u{|Ne1V}nxSb|%Xa_Xy5#TrcTBpS@ z368Ly!a8oDB$mv21-kqD9t&0#7+@mt50oW4*qGcwbx}EyQ=zv+>?xQUL*ja2`WGq` z)sWi!%{f{lG)P(lu6{68R~smEp!Jy9!#~65DQ1AHIc%r7doy*L!1L>x7gLJdR;hH_ zP$2dAdV+VY*^|&oN=|}3-FdyGooDOM-vAGCT@@JyuF4C(otz>?^9!lR%m-tde}ePe z)Jp)zydtP%C02mCPddGz5R9NYvrS6)Bv$~r@W&cP5lLp7-4NrEQDN3%6AmXH@Tdfj zZ+k^}6%>L=d8BK-pxgvV`ix>w6F;U0C zlZ#lnOYYDhj4r)_+s){%-OP5Z{)Xy~)T{p`w1d-Z`uhiyaHX5R=prRWzg^tr8b$NI z3YKgTUvnV)o{xug^1=F=B;=5i^p6ZQ3ES<#>@?2!i0763S{RDit@XiOrjHyVHS*O` z`z@(K2K8gwhd0$u@upveU3ryuDP~by=Xy(MYd_#3r)*XC z^9+R*>njXE-TIP1lci2Q!U>qTn(dh*x7Zxv8r{aX7H$;tD?d1a-PrZ_=K*c8e050Z zQPw-n`us6g%-5T&A%0G0Pakpyp2}L*esj#H#HB!%;_(n z?@GhGHsn-TmjhdE&(mGUnQ3irA0sJtKpZ!N{aFsHtyTb#dkl=dRF+oo-dwy<#wYi=wik;LC6p#Fm zMTEA@?rBOmn>eCuHR%C{!jx>b|+<6B-)Z%(=lG{@y_@8s2x4Hym6ckPdCB$7NZFp_|El()ANXTORs zO@b$@1`3tXjEm>;bX)%xTUC>T)r6eTFtq*Rp*_?%C+fEzT##kVNH` zV}-lw6&hY;cyl5#RR-w!&K4e)Nf4noLFyjiAbKvP7Y!=2lRiRjc$&d?P~!zM@4!?3-vyqs zhm*63jiRI7cfruv!o=zO%H2cQ#o64%*4YAJ=xp~No53pO?eEA$`fR4x=^|*#{u3bx z1YB3OT97ZU3=ol)l`K!lB?~Dj(p_i0)NN=fdgz(QBu>8xV*FGZUb7m4NEbrA+BJ1O z%CPI+T>JPq9zpg~<>QR+je>?{g)rSuWpyCDcc2@rE8T>oNWPiP*u zLZc3LaQVEsC6emsi7DCL0;U0BP!SwAkXuetI25TYuCwD8~Z|M@2_ z0FaBG|x zW)FZvkPsN^5(Q}whYFk-E8)zC(+hZMRe5VA6GZM!beBdDBqq#Rye$I~h@Kf8ae!Ay z*>8BsT)dYB${E3A^j5m_ks3*1_a^uA+^E{Gxcgw2`f7jw8=^DG391okclzQA zwB6_C;;k_7OnwT<<5RjXf#XxTO9}jrCP+Ina|?UA%gFvNJy7HFEx9r{(c&yDZ9e2aovtJL$um8u>s&1k@G6# z-s55RDvTcFYZji6x+UMyCu{&*d4N<{6;H^PEF!?X@SqMfGFR}LYImL1;U}{iT!qnA zgqLCyvSp>>nS}|sv56Dnwxdo&HrZG1WQL_EkC!D6j)JW4Tv1yyqe&aM- zHXlKm;srQVctoDYl&e}E-P8h#PCQNW{Dg*Te>(zP#h*8faKJ!x-}2Rd)+>ssE`OS? zH{q>EEfl3rrD`3e_VOu!qFXm7TC9*Ni&^{$S76?jtB;*1+&lyEq_j{|Nhg&s;W6R9 zB#r9L#a7UU(Vnq#7asUx%ZyVz{CiVL5!CBl-7p|Kl&=g>)8e?z&u?Q^r>L@P zcB6n=#5Wz+@-j`qSB=wD1p_n<(NhAp8wa!IxDP?M&_ zKNcJonwpOS>a3-OBC9jGV@*WND}F8~E_QS7+H3ZK6w&kq>B}kc123ypkAfx`&en&T z+?U=!q?N5DDkt(2$KU;t^dR}IVC|M)pn@S)m{saxD4V?TZZWh@hK|C|n(P&eXLAq1 zZ#v0gPhHJYiyjEkJT~&%u@zLE`Lm!p!&-VAfk?eF{HN%PeV5S87-u3n;g}^R(OZqI zA|##x9SAAKAb!FSr9+E^(}_HX+lb+XLQiWF2UmH*7tM?y7R{u3(Vr<5h8V>Y-c`SgYgD9RvV*ZP{xBLuk-5sAcGP5G zDdk)Ua8PaYS-R*C(V(}4>%>{X%~yk{l3&El7iOz}m0Y8MAl_Qc`-2(z2T3kJ4L1Ek zW&^0C5lA$XL5oFZ0#iRevGn2ZyiotWRIag?#IT-E$gv92YXfp3P1BJxO zShcix4$;b#UM2o=3x#3;cA8Q#>eO8bAQ6o|-tw;9#7`gGIFVll^%!T5&!M|F|99EZ z?=t(Tag~g}`Wep_VX!|sgf_=8n|trl((YTM-kWDQ1U@WIg!~YjGqsZNOrayhav_lrw< zgSle+;b;p^Ff)tDt~?&TweI#6(}<3?Uw1@|4MvG2w}sQgX*N;Q=eD+(bJ%jKJ9L2o z3%MlC9=i-DKzXOun`;&7ZI$Iw?Y|j!RhIn*O`mRl2_vUnE*Rf6$?{IC&#;ZS4_)ww zZ${m6i^cVHNiw5#0MSjEF!NaQfSr&DbTX&tHM{Ke)6Pt9^4_Jf%G&51@IH0aA7QRc zPHND$ytZTZ7-07AEv8Rn%5+<=Bx1tWJSG_?CqXuJ99Zwp=hP2?0a{F)A8HLWkv z)nWbhcgRVdtQ4DpZiw6*)QeCWDXGN6@7m@}SN?Ai*4{l!jL`wrp_lL`bJF6HVAOnj zNa*fTj+{niV5~*O zN5NwHHcEed1knV2GNSZ~H6A+13`U_yY?Dlr@mtyq*Eutin@fLqITcw+{ zgfCsGo5WmpCuv^;uTtgub$oSUezlUgy1KkqBTfdC=XJ}^QYY+iHNnhYEU)j7Oq^M^ zVSeY5OiE#eElD6|4Haq&dOHw4)&QX=k_Ut{?Uvr21pd&diJ zB2+roNX!_7mJ$9n7GNdG8v{=K#ifQnT&%`l82sR{h&TKf?oxK%8RlG}Ia$WP=oQ3C z8x#$S3Rrheyw7recyTpSGf`^->QMX@9dPE# z?9u`K#Vk!hl`$zv<^Wl(#=J4ewGvm4>kxbr*k(>JDRyr_k#52zWRbBBxSsQfy=+DkvQ40v`jh_1C>g+G@4HuqNae&XeekQeAwk+&jN88l@etjc2U0(3m{pQ8vycb^=k>?R~DSv8<0tRfmLp27RlxR~V8j?ClC z)_B-Ne*s0#m}G~_QwykU<`~vMvpTlr7=W&w=#4eEKq!$muL_QJblmEh6*MUg!$z4fC{DBd*3h=N|lf1X7dTfqL1v6~_al z%J+WD;fSJ>TKV*mid$G+8eIjdfK%pu!#kkan;Qi>LK<0bn$?ecFn-b|@+^+OT=0nl zZzN%OUn9w14s`D45>E^)F8?Z?;l!%DF^oL|Yt!@m^V@3twFD@^D5$*5^c%)sM*sbi zk(RQq-d<^O7T8RfFwEK9_us2+S$&W1-Z3OR+XF6$eJl7IgHM~N8sHzWeuzxpB% zE9h3~^*;?_y)7i>a4#z6(ZQ%RaIo)|BtphTOyY@sM+vd#MYN11?ZV(xUvXb&MFg6g z=p`JrH(5;XsW4xVbiJ?|`nutpC1h*K1p~zS%9GcwUz0UWv0GXKX{69Mbhpcsxie0^ zGqgqzpqFAefIt5 zbjNv;*RSO}%{l!Z)c-Qw`A_=i-}4-?=swGSMI^E7)y37u+#O1^yiI2ehK4F|VMVkK z!hIFgJ+Ixg^6jI3#G8UbMwE1a!y~wFx@T(|6G*f($Q=e5na9eDt?f6v;SI;w0g-j% z!J#+aN|M&6l+$5a()!Cs22!+qIEIPkl)zxaaqx#rxQ_>N-kau^^0U$_bj`Aj28>km zI4^hUZb4$c;z)GTY)9y!5eJ{HNqSO{kJDcTYt-+y5;5RiVE9 z-rfg@X78JdxPkxzqWM?WOW8U(8(Lfc7xz`AqOH6jg!Y-7TpXRJ!mtM~T)9C^L}gSL z;YSLGDG_JZayritQkYm6_9cy96BXEf5-2!+OGf|OA7sdZg?o)Z<$B#|?fq|82c!WU zA|T92NDMBJCWHwuFa{aCfTqmu)kwClHDDbMnUQhx07}$x&ef5J(Vmp?fxerb?&J3W zEcoupee$`(0-Aipdr2XA7n`Vp9X;@`bGTh>URo?1%p&sSNNw!h%G)TZ^kT8~og*H% z!X8H2flq&|Mvn=U>8LSX_1WeQi24JnteP@|j;(g*B2HR-L-*$Ubi+J1heSK4&4lJ| zV!1rQLp=f2`FKko6Wb9aaD_i=<=1h?02JU2)?Ey_SS%6EQ>I20QL=(nW-P4=5mvTJ z&kgssLD)l`rHDCI`%vQMOV-yUxHQyhojHdYC*$H1=nrJKqFo93>xvB=M`$}Roksx# zRgV+d8#sk=v+tN#P-n?dx%RC(iv;9-YS-7PrZu#xJ5%k4i*8joRv1J`M_tOQR`{eV zE~<8%VC63sx|_U&{Bpy&?!~^Ce+CNv^T)?diyKrA zu^d&el}PFVWKFz9wkriy~eruRakPmmS0ZsKRiEMGj!_V`HL0FT$ zQU#r2x}sc&kxyY}K}1C{S`{Vdq_TYD4*4zgkU_ShWmQwGl2*ks*=_2Y*s%9QE)5EL zjq8+CA~jxHywIXd=tyIho1XBio%O)2-sMmqnmR&ZQWWD*!GB&UKv6%Ta=zRBv&eyf z{;f~`|5~B_&z17;pNS$3XoIA~G@mWw1YgrTRH95$f&qLKq5wY@A`UX)0I9GbBoHcu zF+!}=i8N>_J}axHrlmb)A1>vwib%T;N(z z!qkz-mizPTt^2F1``LZ#Is;SC`!6@p@t72+xBF5s!+V#&XJ54bJ|~2p(;ngG3+4NA zG?$Orjti%b`%<{?^7HlMZ3wR29z7?;KBDbAvK`kgqx4(N-xp5MuWJ1**FC|9j~trE zo`+jX&aFP*4hP;(>mA>X7yZujK`$QP9w?a`f9cQJaAA2cdE{Tm@v?W3gT&w=XzhbY zCDpADyRHQ?5fOuf*DrAnVn6BjADR2&!sV&wX1+TC*Qk}9xt8KA7}6LBN-_;c;r`H= zwL1uGsU0;W?OEez?W5HYvu>6SR+O8l#ZM+X@T3>y9G^L76W?!YFcytB^-`NyTDB=; zw421!sr`Wwopu>VDWNN>IN&RxE08d0JJZigpK%)p|Ep&aHWO`AFP)}VkqQg1S#TY> z(W)bm7duX(Nvry|l%sGs+Eudz3=_A0i@M47VtBp1RTz_zxlmqgi53tT!_i)(bad*R zt<1n~oT!|>QLmYf?YL$n8QEJ2A6liMI!hRY#mB@?9sWAUW8! z3#M&1`ZQmRP*o`jtHjbA78}!&iq6v&rlp|5&!}O}NT>|10NoWbiq5@7lhquTSHBCO z2a!-M+(e10feoq(nVw~!ZC;y+4M=F0%n)oHB7{BRYdVpeTN zryeS3Ecv^OC_2HcYbRWnOSY2McCa2PfRXH~!iu|fA^#y<&eJkS1^d|DM3)QKAnMe1 zp%9s~@jq$zOV8LQ$SoOZGMPYE@s<@m$#S(N##mh{yFb!URLo?VmR4c2D<_vio;v$u zEJivu^J$RML#dZFhO#!?D8s-JTIP{sV5EqzlSRH3SEW;p+f8?qW%}bdYNyDgxQcQg z)s4r6KHcPGxO_ErHr?P}mfM;FZE)8_I3? zDjMJvQui}|DLHJ=GXcz4%f~W;nZtC{WKitP66ONo4K<7TO!t?TYs_icsROOjf=!bP z#iDYw8Xa2L$P!_IMS+YdG$s?Gh(pybF}++ekEr=v(g97IC8z28gdGEK?6QPNA@g_H znGEeNG!5O#5gfi{IY+V>Q!Z=}bTeH|H2IGYcgh~!jjG`b~gGo!$<2(Kis_p5;(P-s_l8JWL!*jOOFW7(UIXj)5^C~7r z>g7M$hT|sIVBpur@M~;gi~j(BNMp8UkYv?y&{`-sK=@)-@S(2kqobO@Wt_pSnMh|eW*8azy%8exS@DAQxn9~G zE=4(L_gg-jHh5LtdXPgG=|7Xcq4E&x?X2G2ma(6{%4i1k?yUE4(M*Qk6_ z1vv$_*9q$Ow(QAvO;Y5T^gBQ8XX5ULw$iW6S>Q`+1H*Qj+COZ<4PxD-Fwh71j0cBx zz1pnDR}STs5k`ekB^)M`Iu39H@BwM@^8_X7VVp@epjNMqRjF($LBH!#dnEe)By}7T z7*XbIUY>#irgB@|lb)RRvHN^cPT%6slXqX1FW;4YMtNurd;?3g>rm zCSyAc0+aO+x0NojMi`4bp59%=g=zuk4R4o~hTUxxaj-YA z@UtFr6OY{A=_+?qZnrqBO49}q~-hZ!+0QZzD)8F6c7AMQ8Edl-y|d#R;NOh4ukOeId((#ChBKo`M=8Z@5!BZsX7A3n)%+;0Dy*bI-#fNe6_VV1{v%_*=I&54mqAWAg z3XmVyRkbAG&>7rIx23lx*caz7vL$Tha&FcrqTEUNZXhFsibRbc*L@H$q*&{Bx?^60 zRY;2!ODe~pKwKFrQ{(`51;0#9$tKAkXx7c-OI>j-bmJb*`eqq_;q-_i>B=}Mn^h`z za=K-$4B2-GE(-X{u|gHZ+)8*(@CW35iUra3LHje(qEJao_&fXoo%kNF}#{ zYeCndcH;)cUYsmcLrAwQySyF2t+dUrBDL;uWF|wuX8S|lr+Kg8>%G?Kuzxf;L!gZoxAqhd;`!i$5wZfphJ-c zd|uR@Q=cF4N1HXz1y}KjQJ8{7#aqNM_|j!oz6@&wEfq)8)wG4ngiGocMk=1Ft54#R zLyJe(u>P{fm>k_wUn20W9BZ#%fN9ZePCU*5DGK$uQ{GP3{oE1Qd^}1uSrdHw<-AM% znk>YZOU^R94BahzlbdB994?8{%lZ*NSZ4J+IKP3;K9;B))u#S>TRHMqa-y}{@z#V5wvOmV6zw~pafq=5ncOsU z`b-zkO|3C@lwd3SiQZeinzVP4uu+V>2-LKKA)WQXBXPb#G9E8UQ%5@sBgZtYwKzkq zNI6FloMR!lx7fV|WjJ*b`&y_UK9mPl*` z;XO8P%7{H*K=GrNF#+K3At?5`_oXT|Vz!Rh_05t2S&yd`A2 zjcyVJB|#czi?o<&biP<}0alxnpPLzJ9d#_R9(c$2IPXg7=4mL{7WoN>JTCCZ%zV{) zm691r%m?d5yR3l=Qxn7|f0?e7@ zk^9ia@dNTbyi6%GO;kec5sHCjtyr*i1QSY;G}gTsivUQRTG(i)y`O_~K{I*S+x=>M z;}<><>$k8!-=R}>b#)kmSE&~qf+xi@lJazu^F@~pV>MQ3ISq0)qH;F^;_yT@vc-Pr z390Cb$Zq{edB^7W@Mz_+gQ$>@*@>hJIjn4*`B@N%Lt_t1J1wT!aN`jpEBE5;Z|_X| zT^67k%@CVrtYeC}n;uLV%ZSClL-hu4Q5t8ke5a8BZ`=p#4yh?Xa^Q~OrJm_6aD?yj z!Od*^0L5!;q95XIh28eUbyJRpma5tq`0ds9GcX^qcBuCk#1-M-PcC@xgaV`dTbrNS$rEmz&;`STTF>1pK8< z7ykUcQ^6tZ?Yk3DVGovmRU?@pWL#e2L7cLSeBrZc$+IyWiBmoex!W#F#PlFAMT00niUZfkGz z0o{&eGEc{wC^aE3-eC$<2|Ini!y;&5zPE>9MO-I7kOD#cLp<3a%Juu2?88km=iL=? zg)Nm=ku7YEsu57C#BvklPYQ>o_{4C>a9C*0Px#k2ZkQ)j3FI#lIW3mT#f*2!gL4$_ zZDI76!tIw5o=j7Opkr~D0loH62&g?CHDg;Lp^HZ;W7)N+=s>^NuhmsYC?}lxS;sOE z69`R?BLA*%2m_L7BSZ^X5BKaWF-Y?b-HqGLcTd9NU7vY8k|j{O`cOrwxB2WW@tmhU zt`FA4?YCJwFISu42CLh~%e8Qg093rgqDa!ASGd!qoQ1e+yhXD=@Q7u0*^ddk+;D{) zKG0?!-U>8p8=*&(bw!x;E{EjWUUQyY3zVB2V}@t$lg*Bn3FId6V_Ez&aJ%8kzKZg$ zVwL+>zsp;_`X|m4RRvc|Wtejy* z?bG~}+B%y$b6zBRba$P?mX#UbwE{i{@jbuL@tZ6Rn;SCu#2M*$dpQIn$Hqv`MgjBn zURSnq5+1ReLXsI#*A8G1&h5`YFo^I17Y=&&1eQDtwY8HI3#DdGWslPJSP1` z1D()O()qzD6U~BYRUPw6gfc4Wx!am$yM#i~5MCmF8=7(q7;n3?L@7uuvn$;8B8wk8 z3>T-EJ5X9Z3@yH;L=9QFtWmzdE_;Kw^v+te+u`pF zN4&*o>iRKeC&l_{U^a`eymoog3(GY&2h;5vMyRyld37+7bW+&7tvIfrL9TpA@{Z

dy!05UMhSKsK zV1FiJ5SlAhkpcl_H0wRzql?0Qp5wz72o2cMC@utM(|&o0ZO_JpXr+N7l~F?Ef_02md^m|Ly|(EN; z%;)3t6SWt{5hgzszZWS1v^AU?`~Rctor7%qx@EySW!tuG+qP}nwr$(CZQHi1PTA*F z*Vo_ezW4q*-hHnl_8%)^$Bx*s=9+Vi%$1qr5fK%c+Hm4kiE$B;kgV)wam25w$Y7#k5$> zyB^6k3i~L_6~PX554`c3Lxx;&_sT;I^U92G@fS6#(Xv!B%;H3+{e)1R6lyU)8AK1_ z?@>F5H=sXG=ep;kDRZO_ofS}`Jus*Qp3`_V4v~&b-RQ=t8AN5H5{@!_Il~0 zZd!-aH=h)(7CJ&tL%%{P{6d_g=5tsj%S3Z!QxjrLdjoKmNP-zSjdJ!?qL(UMq38ps zjKSz5gzwhDFA;5md5yYb>QN)U_@8Xpjl4yw5065)+#MSGp;yQ*{%mt>12;$~R{eVV>o|juO{Z^ z^o^m@DOBrE2mm1nLgBfA(Wi=X9R%(1UYZcZJ!3;*bR^smI~6lyn`O4BOwo-STsQcyodVA~leg9`{=l(qDl@DCM>s+w`%S_q*PIjYP ziuHHuj0VVW1%+TH*lx9#-$^q&l)G_ojju-w{# zVs{oOc>_fcS51xY+19tN`;V~R0wVyuxdkS|t zC}~Gtu-UyA{H5~6*ocUWM)RfQ076mL1r zFVWV%zx!_*zk`5&dFbdq4nbWxIwAu=`+$V-`m<*-Z*mE2X|>OCAJVV;wlq0E$hVe@&x7V(!xg1*;%`} zxxBu5;jmZEH*e!Rj=Mz|udBR8BR6LiGoLWb<1=<14it;Fuk$6=7YCR&;F+%r`{S6M zP92W>ECy`pZR$Q<6n8Zw1|uh*M=zK=QP0b38_aX#$gB^y>EahIiUzy^MP1ct%UhZX z>FFLVJ=H`FRSq!<_DtWyjLZ6t^Nf|?<69Aj$U0*lrAJG0{t;t8Y^SKLacoR%3EXw+ zDi5T^PkjmJp7@B|$lkEwHHaQ7BGc$})@qNRqk4JH!(bgPM!{Mb&Kz|UGk?QskODW5-NCJ3`Fbks<}%TsOB+e{Hn1i7BP z(XsKkfl`r0N)u1VqaPYGlDxR3>%y{&vYaQCnX8AAv8h8>a^4<#jAhtfa;TdoFlN=?Ac{@Cdxj{YI z!kxobbr?~GU8JKwH2Ywa(#i=Rzof$nu?4-zlN#QJflTO^QkyarxNI<~MY1}jy~Jz` zBRwV&0+G01D9biQ4PR*1NiSqTXZB~NdI6yVEU|AiWJYA>k9G=*`R^VFjr{jhqZ$&G za0#huq)Mhb&8oR!jrv%;xRe@b&PWBXh7ATurhUY7yobngzP;($8b5g z9U{5JMt%fMp(N6ZVGsYa2p(#ry;Y&;GG(DG((_GrS%r&waWuX94*RX8>&x|Lzv8WCaXaWo(3FK=U@G#S$8kCX_R6q|VO;WbeXk~x zmq?NS+S2WfO|{j{dKy5``SRA!r+%)`DCW{s?8uZJW{-4%x}KJzAtiyY6b#)!fe0kA z)=W5C>X6ZLRFH_-$)Z(B8Hr}FD#FLGum2gRluDsrJHf$do$r!ORQqrI6~=-H0vPiG zC2V88MIp?Xhc&UnIS(c)naRXTu-r!%x0J;3uWjp5K%!b_v$;;T0*{_2txs!*+BgP} z%eY2;N7AFz(g@fFy&(hWk`R9#fRZ&X598A7xjHyoDJ4!3CK{Grr4>0bTBw3ps{tN7KqVY^)~B5St2NQS9wH_Lc=s8$1H5J?52_$nh z+rnm{F~bVIsiCZ^Gy&eV*X9JTJZB^`|6F$9|Fq@ekZKP~h_BWGsow^hUpo~MCTrdk^1B;= zNXiYAZnUPm>}{vX*&Yb&{0FNvW!V)h-<{na1yT-|kAkG7xU7QA-NAc|e4Nf2`OWnV zxbr6@^wO^6xW+Xdu=Z{sdK+Qw3Dii+X&Y(VdCv>CFEIOt?MCM?9@CDUKm7+N>%!q z$WI;(L@2YJ&Qfwr7k@<77r}%_q3O8c#><<+(JFdeT2?e+nsP4h+`n(HuX8^8qLN88 zv^9`|ICnNwS^PYDf7ebCGG~QNosD6-%$5;6Yx$`PGlZVnxs6ntftJW^L?iy3KIBDW&1q;{OspV)`a4w`+K45XmW5g6HLPL(lu zM^>HAPux}=ZJ?|;f=zDh!2|)WLyu7pHcc)9vAr(R_-sI`3GRfExjVpYMgql~xox)Q z)W3=WFT93oMdC)bluYO{cphI8Hjl&)W$TKN(PAk2r&mB9-)@%@xbewYx!c z{}phewJ939{qT;q&KR_!>>XnVYPC^kRaX%+G_v;*kg4g0jdi&G2G5$4#bk+*0mK8` zie_>y1oDA_0hGE(n`I(s0k(P&;*KDaX278vofbbNMZ-&1MCmPD*6d6oN$VjMzpTd@C8e zg81s83_+Y#T;duYQ%tXE$RWVk=@P5Z1VY<1C?mU)7?G9IHYx#rHCx1Mhb!ajXBoJ-rANULXqSAu0Mn9s%@_;uy-AOG|5#jDZ3j5dR7|< zR_{f>x5E@uRa$=rDD-yel$t(bf5=#v9ZWObAu%fou?4KkV-kvjmRiGX7iDe(Q)_^=>m}`2$#Xi#5CpJTi#5EF1T1mmPB}c@A6ou~a`>sHSeM4gF(ksh|DObX#Ao1r$Jp3I3 z-#zhd+d&)DO54E0K@@kKgxRB5%x&3BZ$OrawIi6~b_kN~$5G(kH6b5BD&%g70UWu6 z-ub`EccvhA2YleM%U@;V)N{Ixrkd0bjN}m=kn%!g%wE&P@WcBs>5NJ~t}y$Ar7F1n_=iC*<|&`C=qG#+ z0|)?s_kRK(@&?Z40!~gQHirKa2ua%+8CVNj{J7LD3|*Wp?EV9bZ1_j%PH`5U;9>aTZzwPD=a zXur{4zSk&)HrOFOmSK8ZKMHdg*HQk|a($OZ(0puje1K8EZNjPavWjhh64i-B(p7Zf z2g`IQ_W)I`lGa!LCabrDUSVPmGZbVX*#xhnAH|koEn~hs`=w;zVM^IEU${9oXf4C9 zk#|zrR`2_TI+u08MszOoi%H;viD}|x@Ax-{F_aW3ZIQHw-pT;hgNi%weuhcB7xt*kubK4fep+r)eaJIl%p9|sqv{M(E4lgwXe=HL2nYvO$$HX>QpPxqUn}WG zs*l{rztHOO@k5#cP%_alezmlZW9HCcT_;auQpbtV(Kh6e(9wF`C;OM(L&uqUaFglN zk@mRfKGV716J9j|zU-6W(m9pmEF&sbiZMv*M3~8lC~<@%sH8mKCL5zS4h--)TNbi$ zGT~m~}sa$tL(& zG_GBAe(+OZUY}-iY-rcb4f^fNZt_IXS52F^MC6>C?-IuOUttpxwVQBy0~D@|I1g*pQ^8D9@mu?5(kge3_GjbOm2G+7-z zkx`X#L5jF0+(b=RSgOE*XGFk$mF562Yft^UFH0micC5KNH~tfuDq*ce5Q~fKPyieC z9su^F5Df-F2X&FrZ1?<8uQ5h`uh~m z=&m+g_sL;h^%^JcRk%COiklbyo`Co8z9C%hj$&e+^pKMm>7Jt({+@)$DJbC`QjMHZ zi%3X-hLW4Gca)8|Pf3A1t4Ud8Gcj`ZNDE=lz<+3#C9z0jMR_q934+6jFXzJ$uCq~+ za-#O3p1hSU;tiKizC8=Mh@y(Ne3L{f0B?%ewopC*gCiXqueXVpGg9HaGK>hK#}F8++%^d7M6b=5@V(e#PAgrUnD^4)b1JPZ-PGNWqckW?kadj9w8b7f zp6l)!4JIwHtcBOekEW-B`yJ(E6n$+g06FFIjgZzz&+`UpKdgY-=lxNe1BI|=Cg;T; z?FYQs{*)^&tV>xbx0m~jf7l5>`+q#>!*0u^UJNZmE(3w>j|yNHB$#6zkjE;_0pL0S ze2gb)=zGHVUt5ge;3k7XmZcc5;mh=#z-ZobkM!xX0De$bw@9s|&m~zN9 z!K5tX5=4qA2sK|$bdVMz5etUdXN!`}2PL8R7qLr)Si} z!IONdCg$e~UlJ3u{n50K+;kj7SP&tC(^xDUbl{fdvL#ilA93{7Vm|&0)1p+nx=!XmT2qv6B?FjPHZV*SamC-ro9lXMAbWtsPx?Xq1Kcc_^$@r-YuI4|#Q?})HOyhMfBUVTIsc4Su?*`>kGqVs(0tbI_r0@mbv4tR&NZCQd@%?W!R_Br)qtk^~)!$ zd{bZ$2k_tV&)c$dz%vTer6*=naysJcAnpE2vboBzhwzL3ZZg^xE_1)_2eUw2B&FcL zW(!+zg@=0oy{=sCi##j;)Rn!Ty7I5A;QytP@}FjBaRXc9p9bUK6(&VZ!%ayA`L8Y0 zHgiu1Y%~0(WC8`wPF)OYDg?-xhpK#kN37I*3t$V> zeFT`E`_n>;_dQuVYN1PBmZ_}9TfEcl#^=`Abh1!Ek&ykSp^2 zUtg|J2l-(Fu4-@Z^fZW1~i@QYwP9Q9$d-lN6U6i%K#778wN;pE7`?CIfN* z4j%4F^H^LF6Q70%gi@GEB7#Kar{F)1=Hjc!yt?q2&-sWb^&Mo@Ali3 zYsI8ugwjs$rA3@sca{d2=a5mZ6PM=U7R~l1{udpZzpk<&^i)W$IV*$FUzyJ>#@G4l zunDZP3O}4G8=e2)DEXo;q|ooRSY*pQ@?dPnSA%LBmzMuh zj6iCX{hWsksbMQPykb&WEA^2^)4$ly11z>xG12rAj}?8Ft!(tswaOoNlpt=|kqrTJ z&?vxxBG>4bNn(%_w*|gVh^|*LD_=TzvKLX^EG3#)_JHhIOGSwPo4|0o#`B(-!+g_f zebxHKe=60kQz4i3=g8Q=o!~GyJjpp(m|JFSl$~J?ocx92m&&RUW=F?w)i?X8sjbbg z0+7xvpM&&Mvk2s6TEQh%-l$+wW+-wwx(yPsAW>CS<4@5r)9$_e^l&p0?yxh8t`Ni| zvkg20%R$9KD0hWHDff&(!UL3EXA@7RAORZg2_v!tmF`q!lSi%o$>srm>6H|S)B^2X ztV|vT66Q&WzEYv3LCrtL@fFVn_1u!3AIwvi9c5g^-LY)$kEOwFcdT%;T!@=Lh3b{K zJ5DKC5TfipAQ;Xelrj5>A z=_T7N`9+b0vmdY_zM3SwtpmRY?wNX&N^VG?5}z__+A;qz)l|ZX+QaujvNXdiXZ(V? z{OmPo1P@Yd;$G3ic^NHAm|1j%cIXFahDM~236V%gF?}nu9!H?ApHB?XA?IZs*m$xN z6e^ufgCQ0+_=81#=-f_IGbvy4Xizg)_Q^<)baO)G5(DO zgxn}JpKET9(UqMupTD8jB3cp z4G`IGH%ByG7iZ-QD?Esze`e049rA`qU8-l!$qPyeHl#z_q%CNdv(L)XI;?Ng4p}qk zjkLr}p4PA1I;7{Kc1WJp_Y!Q55JqK#sB5nY)=dehb&d)~g=roafxSw>Sbm)`xVXcf zG#`10jAW<8I#Nd!Q<)M`*0YE;dZ$(eKex&V5$dNnGAi-clRskp_SX#aKy?8;Y^RA; z@xEcdlr!iVGK@89*}AMBb@T}NL#V3*a00ErFr0GKMbDa2oQ-DkTV{N0Y_X9!nY1oWN1B)$PK)1Hfas5LPvtlH8ZL@g6sQ;=~> z=vTK;Y5TAt=ya36;hG?pES_n__RRVv!qlpCcy$N%vN$cm%p@=41Lzl*;2C>KsLXaT zT7L{$DZI@k7u*!SE|y2=Df|?99>gyrLB^ur~Y)vi9TpSJl6Z57d+o)lQAdh`R5kMGB7)eE`*Q;2G zQEcRN!Q?$b+o zUoag8iRTMmKuJ)5s&zS~S*B1~zU7tUT|q&h!EInBeZf#vwR|05>zpU0zRe0VWg5C; z+*3eGa6)oAS)jk-xN&bD5&{yx=Oh{=T<=akX4F4Yue*V0VM zkH4;7TLKmx%@)s6c5z_Q&5qaRX;$2vIP-ud)H84PAd0uJX*ee_AkeYKVtI6CW@W(9 z8KHRBux28|zpfOJu7mRVm*s z%?_&|3rLG%MZsk-XuimeAl!(zkxHX`$uQhJ=7%bztEXtmw!ImA{G>b$_T&F%g zFsQ^s?i59_UX8n_!c>ZltM6ABcMHOtRyrRBB3#Yo+AYyiYjPIXgd#0RF$%&xX*?+- zsPtBuy)cPjVkYkf31o50Tp3zUe-dekc|5FYz`%%l5L^>Pje2fT{!AGEHxWG_Yi|{!_@x>cc6%5SD z$ZvA==C5j@X;L3MCV!XA?SG9M0(T#83W28(9aS(t{d&siNAR`PZa(ke>q+Bbo82ut zvU5xmnR~F1ffCpw7|Fg1Gx@$)QGYDzf$|nfH3sKP3=Huhz#4)dH-ay~7cR-ML4hxY zJC3AyNh<#3hBqDyFFY{D#*eE*cnh{slzoT{|2On)ATR!sO#t-^ABA9?$(s~V<1UDq zyo>|Hc*Nrxk#`IYFkXaDTnoHWAP3E#`a^&-`SJ1RcPRHkeTbBZ&q3G_0==kIKNsi8 zPK+SND@w;5@(Jm9!|;LDkth-G0@RZYW&YJ3k={qg)_?xtrkih&RnY!V zo$Y^|7$WW_MlSzvW>1PbggdqghA-L1jCJc$kjxUIfuHEPj zLAS_=)=>DNjluF!EIspf<>8IN^gzw?ak~<)+k{ykeXo%GE=68f$Z;ZaxUAiN%zGF_5d-JZ0I9JZ*6=&gi*5l3i_WA7VrU|K{v|a zF=S?&Yw?$7*XrNDug-5bH}qO#ji37gcoNsG74BAO>OHL zJ+$W5wVs^^UjrNk2QiwyJ(aXP&FiHZNvXoDgPCs;lE0r3q^E zb1QZFSr@``4tbojlnOSCOUjP5QW*?2!?w1>p3YwB&Mp*GO3M*qgz>{jv{ak$b7(E?tkY*+R+^&>> z2dO%o%W=L!QGyw(WuAnw#oO{!I(8KwC|wq_y)<9lMxDiZwL#OlUU_DnD8&!tX&a7f zewQGgB8{dwkjR8EC%AP&bY^iirN#jA47*}#6?~g6@a?%^7(){yv(mgF=P`2yXr$Ab zuYEY=Rw^DeYTFZ^Ywa=6!`PU?q?O*FI=gFl`bbPev2k8T+=C;_X>sLJQt7BpOATpg zrpfyxa?;Uc`KUT2B@@q5dI0rCDDr{Q8d~En$h%e_rtAvjTEMd-OH%Qc7)o~}(R!O` z(i0MG6N^6LsC174qc^gK-0ayYDy1n5!q9mg_|@<( zH^wGhrdBV;Qzf}LA3=l3S|l{2(ylqgc3&K7pj~tzGSA`-wO86b&05pv_SO)Zw_hfmjx}wah`^|Qo(J(X2h!rc zPxx05-j4zshLMr@l7%0`IwPtjmgCwA{Sxj^m0H$vopZOcn-(l18gE{v?!K>bbY!=G2sL;OsI!wlS zl`om0y?Z#6@8vtXFRh`e5wNSy>T)H41%)Nt*jt9t?c#B>nBknI{Kbhq*5+Q8Lxe_H!J*!N? zH;Gr-bx%ExZEmt^9#)xcGN#!|?Xz6|l^~v7U7wM4&5cAIxbMj53pOBXW2LxqE#=+s zUC(EG;8)Odp&Rd)Qg_wrCnDExg_o7dmilm!?}lv0f5NK>w#Db7WRQa5Z94pw011GV zyHnjESKowJ&H%GT#al{iWgq|S`7S)99~4MXM?gl`=`rD9WWj$*)*NbWq$x&Jdq^ z(Q<+*Sx9NqE8$^Fqc(bfoIHwRM8##C@jW61>q;vG-*gk8G>_$;P+4b&%lQGl^XQpt z@48~+y!wp4mqN@Q?HOZ!Yr_;kT-E1R!Dz4OldNG)t;&2^&}q?~dMa&r60E7E)}#>< zrV*SWbim~#un~*J_!+nsWF_-x*9gTk>Hl>g2f7!ZQCMExX9omA0+-Fd%?Ek`^u5Av zTse2a$3`W_+4p=xIbdWKo>d*OlH=zIocE<>kNpS;Lx`OQ&-Q1P$CASxn1-0~RGYd=l#b>XT!xg+7u%F$Q7jSakj)eTa>Ty2qji4Eb4HFzvHy#qP|SXp zeb#Lbt?Nt*I~QuZr{s3Gk%GGcNPV5a16K0EjBCtb^pLdk4E5uLHP+1tY@v3z5hntx9$Vv0Tj2xkovNOuQz_TE%+7VTio)we=x|p6Zw6woNPx zcG_Z2O%BbGxfe9ld2ol=fLGR4aFV*%y*3D#mSjOJI|7z5B4+&ACSoxT&RK_fuBkxk z1Z{D-MxPSpq+f$DN!oyle^-|TkMi;fqFJ1UGd5NFA{AM^B_NurnPV??jj4yDq`QF! zXQ%rlV=SedtGKM5GccN+LZ_zY*nRh^QhVnOGA2jgF~DjqY%>eUXu}5pt)p9N9V|0Q zXC@$-8kj_9y)dSR&f2Q-S$t*V60-4m5IfeHAp)(*?%V*RU3YRI+fVm;XbrN;Znfre zHV>~Kt<08qOPU*d|3s=CmW8uaSX^bMnclwZa0*-JYD_xdlH-9QSVqCTFRD6%n}VS4 zy>uY+r9H8?BwSa;PMf%#`x7lDq2Ra&?)MJ=q&X-Vdw3kLg=AF;bh`Ngu`{SU0AP{2FA1bXzI)&Qc+N zQe2V^EkBDVUja~}gLyF(bfSN%OWm}6u4HUH3r`v7TIiEzS4!DYc1O$+O(bDf_b(zmfoP2*iYBPA-5lKMee z{!TLNugW*re`hye;8u`de34Z~ks!!LT7(P~?WfwY)j%M(rRlsVfY75wv`_j8-f<~Zh@@_No5u3lgB08$gw3J7t6YYm|-P>#mI z?Ihgih8w9<&jhN0?+L@xpaZf^v}|(+(B!Te$gx^{k_-y^@xZ8pvz4Teo8$&XcRy}gCz)E#b#7b-MxVm-OaCXYoKRhcAIJfQDELSMoUPZ2A zGJT9WYcGs3O6S~oE52|3o?hBGjTo}Z^#p~Y8HA5Pg?)uzq1dK9(?}wqZwRa130=%H zYf~z=E0yYqfTG0fyWBEMhY>h2^w4T@H3nLOIgGoExay2GP9=7H+(sF!>QtGs1-g&W z_gbac+_K^zlCn7G0blgrvHCKoOxX2B-RbMlZrJ;wg{CYdkQ}uH=vCz{^XL9b5MT@I1LRLBCN2G_*J_s4ZGh zWx7MbR#kfA8X5^2SsOa1ssX$FKr+_smpYMtr_8IC^|BTXp$X~a|@aOR`r7XM(DK=Ni-`62A>;$AvH z9_f{d2&YCRYk$@WOzak*c~OoAFfe6f@DJQ(UOb0(1s-V6+8}t zM%Y6TDbM(n0`0~e(Z=fVgsQi^OTtAv{cQHYLACfn!I5^C`4kt?8a_m$6 zbcTozSL$v*0uQgb2#l)xk-#q3kt{M?g;oWD0s&KKtKIf|mIluc_x>!Nn=F(UZhmoC@MLVWfWf8%A{!LJ-a9ibm(5(&roPX(GX)q zd@M1x1j~Z)riLkJ6l^njEwFgGs7mySZY8C9vkvltS$4KH+PxmEb7GD8$Z)quJ$36>!5YC6H4?tWLx3jX zL_~2klDHUK>j@1}T+ZgC#@^9#==euU-lRuP-UC^5Cc+L8jCGOV7-{#UL(6{hSs1p> z-8|04uLdI$1?;BBEEg_BTk#KN4^e`X!u!4==E(^tnRt1KV|!i-9k}i*QR9@it-?e5<6jq(E{}G5amY*n+H0gn_Y9 z-8;^pTZ~?CK_9>Yi%5S(q=#!=vps#u3bpC*N25|FGH$TQ9Pd_4r2%$YW!S{i=_C!G zD_fX}hHLaDE%xg_fp|i?KbzndD++)5bCZZKr8}JL`2AxVDM>tTh|-T>%j~EB_}}&( z|K(H^a5QtVF|l}x|sSOHm@dqAK_|9T*4ARfIiVq!E1 z{?^1IHFL*xX$M4a3Mm5YU!EpeD1oBkARcKhJu}}&7N2i-A0U4zc4~oNFEZ@*1*d{J z{!TQ-;$6U&WxGgOjF^lV^S+fK(41yMfFZe${01$COSKm>OdY0Ko`nRwC?nIcv5sS48^fobUN+7gD3h<@?TK=U zsq2}1JqYJDkDjs^)6H3!Y^(ni&NTu{w6vfAOZuc(I-NvUIA5QH9(Sk7D2hx zNiT)h!1lkZYyV}v{?Q|*B<@K93LuZprFU9Oj(?x*`7jTy!&B9yOv zBC(n=8x!WoL6TsFoU<~Hlq~@JoFJC(_I;+4<3?2gkpWZU!T~EWMF7v*q|26`QcQ^K zyY7tY=WEzh-Beb}LTZdzTqsr?>f%%?W^OSKq2qcG1lkqAukEF_zkk$u>XCWe4? z#Ea%vy>ICg-GEoSljel7W)-xQqU;Q+>#pyscZDYnsvo{+1MT9<8T4`~uVdxf?M~|B zynet59NiL z!rIjSxz;b%7{vy1l_G16WSgRE^<nid77&vHB`Hc!j_1F`ZD`0gi18)_8?o51 zU@6a|ci)iO?`1pg1#z@MGaRt#+VAApkLK*L@84Osn8n1p&wayu_RhR=UwwK_{XRd- z@_u3Wn-N%#fS{lWoezfKS`U=q7T4pO{SIjeFQMNZYxLGubs&kZYA-$P^!^hNiAC_F z(&Wq`HKids+xS2b*p4AAYkL|*f4oYA(x!rpT&_C7K;2ZG?{}K&D<-FkT@)`3VJ0Xb zH#wfssnie>s1svHRy7r9dzwfw#yY({tYB*1nNx)vazVXK$6z6(v#cyYmxjT(-pz)Q zmT^!`Ze~41QiQ(6|xf}+@C5ZNKgKywZ9F6&s&=xLzP2GjAv3Y0oF|N9sQ z)#f|e$7y6jIc&Qc}%ut}8+Yq?|zk-iAB&`7zddtXt^a zODQ(DgQqHOTe)pS1jRV(Z4SSYxFFm9bj`YffOXR_nrFrf=Pmfr^F8?NXDAH)RY_IJ zia@*!T}8>IHGTVN@d71~NRP5^{UuSEQBA;iP@E>vHBrii=Mt#3LM<}6v(uCW8I>pj z)iuPfGO41XkYTVm86?P+ZI7a!bu#F#q8E#ld66=_3qe5(7rwYzkyP1Cj<^O27m+O1 zqSOMa#3!)|Oi}&%<#TTC!j#90$`EUJWnuAw(DgEXbdGZ}D3-~lWKfV3CT06jARCpc zgW3?!cGxC<4bPFx>G2K|pQw6%H=mDNJ9f0i7Z9 zM9Op2T#uZC_CRl%l}%9a`x8xq0TEG6nyJmw%8@N+>W!pE-tgq@Th2AO(m( z5h}V(JEs-EqPp`)cKevppHePn%`Qoa-TTm}v83nfYu{=X)eka!5~;S>wiZ9KJjMq6 z>Fgx8lpK|M8rEmK1%a_jTLUsb8vpPoSY+$7N+_;3vCrkzy8E~s*E6qfhheM@ zrP!Wm9FgoRV70zMFupOPdouaMx%rka;9iusBffkukbq&Oa!Av$T*C5wgjUDJqJ6aB z(?h;NzQ4!^wA4Jl_hYZYcSg~3H}db;N0wk864a3n*J6lB-nb)I+5y2n+93^b!`=_} zy?b!&O*YX7-^{Ztu`4-1**M4EM4h_wU2-D?C}Aqy5ML7Yl@D#`Ppq--or&5LPqq_} zTx|N&G1%{D- z63FD%(!Xv4BFxTlU%s)bFl{J%a)l zqbCh9*g7WHB#?5O@r&ddY*myj&i_IQQSRbI!%jx#TIh8Iq)wt}a5M>>xO${;MLFTF zQ_O(@DdX&)d|+07Gko>hSrJy|%;=1|&mC?0hPHtn%4a35agZa4ED#_egj-4`fBqo0R#9mQ#BIn&i-6N6{L`Zvuc zhVM*t=AS0*G3(^>#-9WE*H7jAAN6DZVp#r5)s#1Ibo$Ty%9LoC$U%Pi5WROaGDy=C zPt+z^E_YxBba`ZMfei{n!7?uADyKFLcYluL^~1#!m1QqvZ}0E6J}Q3>QHVrfykO_w zv$|82jDqR3+Dr8`t0^fspZL6W?}Nb;in4>0ln_bv#S{!mP!7LHENN-l=~@%6ujbu+43{~BuZ zw^SLl6$KJ<_cuxbNb7Q!O0hDnWC6M4;8A_GNy9bkmdF>;M}Dt+#2h+{u6VQ^>0eSK z?k25<;(Ths!zu0AKiM3QGv1%~7fk+3?IroYB0MoYk(mh#@FSK8vIjI`ov_bH&I$oz zrLZYtsUQX0EBOWR#C}5l3RW{%Bo}~%2(30eRFFehtEwIkdu=PDTFFsev{oQPGaF9N zLO7CGqMw|o4 zXEdacLL>~Z9Q8;+O$?#CmfUc5aG9?YnHuPISSR3nZ8JM_D8dyb$SQv2-HWX?N}@nm z^pSjPE?!b&xN4pT6Iqj~IYUn!w~x*r*YJ!DJC8qDd%4PPqge{1d$*@GPtr)Wz z>kkUX_B@U^7XN4)%$HV&YAuDsY&6oUGVU~47&0HNr6)8$M29v4AHrT6Y7amNwe@2$ zMSs9J#(B)Opvkmq-rs#zH^A-}z<5I6p~|}zU3FOP#3gE}fPLjmm(O>k5}KVb$R=n4 zvES$OqRV_LtbbnFs2e-~T>F$+Tee&KFz1vD>C`sQ)TI=mBR(H3_R%|oh4VtiF3Lw_ z7tdE0!H=H2f)&ytAwMlWbDnuG(ULf9m*DTI1h-oaT(SX8kWAje29U8iM_5m`S?wCh z|2)fTcQ|>_y8p(TEt&BeR`_UPS^SO_Aw+z!Pzmz)2I2q4*o0Z?4L!A|{tFwR-u=j9 zsk_AMkBW&!9LF;X`vOexf?OkPMS?qF1or}T8%dvO4jne0W%dkm317^C;}z8p2F%50 zC&$arDGBdTWteETu7-Ej;`Eo6}jy1~TUaAs~m zhhS2-ZEu)clw!Zg9(sfvs-2Us;-4ssADLua7E|t`zlU(bj*`I2HTml-oa)BD4e;6x z#Il6qrF;-Y&tW8D@woFayo)8iO4hl9<<`}vd|k|mufrz)`$@MDyYyXLUZ9H^p@Jxe zn3mtSIH_Iw3x1|2Uhj^WaR8u^ISw=>@4vIf@UM=kjX!9O{)a6V`2W#l{>NGNfA8Xd zH=IuY-n}iVHvby@n;Z4Nh6Epb#M;g4i74tF_sb-Rd>-;(kwu z!RK#BjQOW9?`I~}#+8PwCNmj9+V$-8Ece{>&Gqh|xAzMwe+X%;d4~ahM4=pFn5%J& z@T0^41a(ePmuQCKNZXc45sKg7Sq99%CmTnsy4$U_RC+C;tYjWEXHr!g4%MNwS8o=t zU5BBC4m*jkf0GUk%P;RA01A1p(jYj9Vw|c~O0{}Vr%@Vn#JfdxEAB5UcKs;NtiXs5`3}FZBK{*S)g3 z$55~%jX_?tZ2!@XL*pbtJ0W!BhNlhcAlYmd__dLYu$LT3VyZdB7?{G*%+mk){+zJ4 zs;d!SlV0vINdFQ8yIDmbS|~){ZQ+Xl-0nVjY{WBZH5Ok(qD#50@k&HaWJ=SGQjG>sw?0g%xYX zo)I%5ZHB10EwcdHota@yKcn98pHZ*azYhpLLnCWD!~gxero1VS zp@{gsIoVg3UI+zeB3s%p_gfSf;DeNK@ONMnGm*)fS&4SKAx4v=6GM980?4Bv)-VW8 z#%=F+UKG0m8qZe7ZTAh#?Cr)Tq8}KQ_&S>Q)0X>H>+#1=Ija73_V>pJg^y?j*~!oY z-dh3EgHGCh#cwnQaC#T22>X=76ohcssCz$4SzkX0OcV~A(0xas~l-q|+(dlYU+po{VjMHA~h+?A9sV>Gg8pemGtgwQ5AD<1!^m1fsM?$4U=Pdx_dA z1Vdd^{^<QaRq{WW`$q8N+3kYCzjK`3k>V=-aI z24Nj-l1^-9@jCMfs_jjagNd?f30jHf$A9_`|w#Lm3Kw0)GM{<}zxR z>)9>F0>Hl3fVi{#9s@Nu0wh9jAuXw^`{pc}oS@tT^KC?^x}q(lC%Kz#g8xDh&VExs zNwY#ntAS8{_V% z>+5d(Cat43U!n=EJ35}M^%!aT7r^byL#@M=>I%4i#Ns}GAERjzpA-XOl0L$U&V?$O zU5Et*b(n1e(Qj=l+Kt#miKG*{HUE^I6ZIRiZkqVvq{2)w$2r|dfN{q6-d5PiP=H>y zFfj3n#fJ%9Wti#CMh3gPv`;=Zu!_H}OdwcEN1rtFVw`_} z_Z7iZ!2v$7Z1VH$Qo_SQ#Tns=?5 z`x!jNy9?0?NhcNi)A88qo3M6Dd#sE$?1>im5Hw1V3NN-b%$fzwzRli)mN1NdKEb(pdIM^yv_VSLm-8J|0?3wwKx390yng>H+3*|GL-*W zhqW^PVcIsjKMvvlr>9Td{6EOHk^L&Om4yV2S>uv;W9x#II$Ugm-=BcL6@dv|(oORY zX7m_FEQ`+Ch_@gwICp#EKsW=&-ti&EPRU}DiodxpG8l}z?0>$@*Qfn^lwUA4vHp>T zn8Xuty_)qK^|cm#L>NdIiWn4-tCFP#ErT)SiO;BWj^5g|5=@2g>;78mCz@MVas?|7 zTw9y_YH6PE62ZarIw}?Se;E~U6>#}oDb;e5%H*HjJ*!+#%z=w@6J{Q%VSe+1aY$-A zYiu2F<=VJ^sE|Gv9({JrR4pe`8$PwHv2b13V1af%!1$s2UkY;kRS;<6g!xUC8O*#Q-fj;-J7t=$q+gn)jXnj( z1wxL)j~-PE{e9s9bfni~T8*~RgP&P!!_c?gcR8}vTUg>9en5>d&RK=wqPzDm#gp4$ zj01f?E#o{t{#5aQ|3r&h{ZwH5!#4lnpFjQM4u=2m&Px?_6-;NO@5vh4aaz$4;+Vfo zXzFr0t(35F%ut&_KV4xqqT+;eWs@}=fuc#Njz-9FE@W#<@0CnSrHbWCOXB6BNkoY5 zx5$>A@1ET6XYn+j+&CX^rNsROBZnuWN+;2(HE>lR0 zdt+vO8Q`bJK=B4C;yF_|RX7V=U2w9SiCA@8{v$N4F98y0ULq4>-vfwx=hNc^ke)jP z=JtUX3@51;5GL@pCPIo6e?R{P_1Z&Yh~!3;`{l=LI!TdT+GBjnhRsd0E4$?t(cF!z z4~#=v5NNe=^9uQHzBg*}*h}OJs4&Oz+O9l{@=ma&6>15fDnS3Lu zhNjlUH_tu4aG8~G#M(x%^W-&-9c^k#MVC8F+(@<=A-S%`Ub$W?Fc$Kt5+9$Idch*` z8DPZGrrDga&I@4J#R*`!JUMdw*O>xdJluM;2O(QyC6bm(|7=LXtOMpeK2{Oc%&@VGgIM}n=xPTsHZu*o|%=ydsHI*DGc2AD4b$rWMYr_F+cj(?lYu$Y(d0;`Gym zsVB+o4{0WaVAxWNLo&g-2maMO*qGgJH^Fz&7= z2fEolQG2QIcl}C3QYX&n7uJjBQw?>=S+N}$3TvDBB4GzLg zRLYKx^=)OTX4DgErJ$67t1~NTT)b{xDBJpm-PJp6oYIFy>k5yf4es3Dl0RBGlcl=6 zkeqZGj7n2lOVEiD7>~>izlNL*I0?~Dk3B&I=?k3@VF&JxNNflsY7~FfIS1h??ud;d z(DEysJz}!|k{hFP%wR_V1vv6eo}VD6bZprUiHm6Oc!Z({ZoD1T7?|r-)XyP$bG-Kk zs+K#Tcp+0iFn)Ojr~N=xynz_nO>QaMQGRLk!77)=oI))vu#!h&Wy>uG*Xlp#{1EDy z%3$r6jdxpHLNJIgSmO)!3NMHED&BdX_<))Ch(?8pE>b8Lyn%w;OM+3lR+y?QTQooRsb|E)Y+ibYPpR&p z6s+)b!X(VTwzS7+!HF5!N~m_e9HxfjR~m1(1NVhmD`i`y54ph*TuOHuB+7D#w|bn^rs6qM}j4>u88m-909 z8Qn378h$ehryt=81-d2(punML3ZG(*KwecJa-AGkfNPyvMS%^{9mNgCm4!IL&HC@J z^l77MMF&_St=`G-5)v585Jn?7Ln~EA!8Fe_82Ch>P0PpQ+VT)sB9MB@HR@Z3(I;CA zJo(00bBCDqE0P=Q-p@S%iEzyp(jhvEEnkvBeitFmh~)w7kJK)2IQLuSThcG;t;19m zA}y3r+ik(BUg}RFoeS0@+Aw!O=T#}{7vd=KmTSobahGQvS@-iPF`2(zEWZ|rcL;+h z*A_P95X#6hgKb=iO8R&>Lx(@?U7Hnbcz{}VWQ+Y_<#T}WigYMJ>43m!22#ZMp5gld zvjS`{o;AuM{G5Q_d%Q8HaIyEgX^dy2Nw)g^$op4#@1uRb@iKc^`0oDIN}!Mz`O)-4 zeusYO!vEkuT+-Cu{)g`VLl%DQ1^)|Es7&0Jo|i!!?smr5TtY%458>ez*n}wn6hK@k z`Jf#NB}A3*Xpcyjt>2`!1o+JMh!McM?KR%_f7^?f=04Td*%F0@2j|n!kd%~Ws5j%c1tuc1<14SI~GT{=5FRz6U0JD0S?LmuiOd&*a4Hl2GA3j*mk~0 zHG{zh;!{+DZUTEyhhE~-I~nx~s|gCSu*A?HC1m3($CYe+6H9wDyGls11or9(nytJ| zd*-n%2D@K`5fS*rJ)?+*sq?mMo6t0*6fGywY7RRNIp4Ub#|f4Kahsq^&@5tt_sEw0 z6$tBs!r=*u#H5mic33oSM;v_oggvkemK}+&k^{?7?z2fqgf*5IzCiS_fY*Gr3UPfh4gBdXY(XjrTV_9xzp6snGzFWJz6*U5Ae z>b#^$8`}Oa>Yx%)Z5Ua^{d@1j`9<3&2(qX3VKiS|pK-r78?u0jI73d-73h_vE*v9^nb#_S=Y|+zY*z1#s8FFs5YJ2SHfgyTzIL#sp<+tP{L67dQd6i78rY* zPo1dBFRd8bfj;rLUm!egc@bm@LV0>{3_0s5RelFi_9kbtHD7z!KV_t9cYA;Qp^bbc zltWd_-A&ujR6b=W(!+E`0+JwY$>sB{$|=DQjq@`FVnLG&nzyoVm#wvk&sDJ%kUz$< zsz`N9uTKBzKyxY92j4VNeFI0ST2*<$kTnW%H&05Zz(!w3IP3>SMCedaI4A zV!|4#j{auL*KY|)(UQMQZG@D-G_i}_&nIGbPs1fosoM8gw&|v0gvu#GWiJny6dkAA z-tutWs3nWft)s%3*w5>H2Uz2q{mj;TB{`%`((Z0bgJ@|&bigU0=wieD!l+jHeA2opi z+<@NBOcX&dBF*y`WU)wDjBvt|L{|-1lJPd|sI&$C8(Rp_U|c3sZXHuWY9QX6;iwQ@ zLl)3S<^&wxggq*BjIn5v)~&}bg&vOc?VbThy}Qj`JF9KRFi;(X#(;=Vy)XB6dBV3J zDevR#SQo(;_9_)=xm+BwUe=4x19DusZ;98PG=+T`ysxWBjg|D)oYj_G%rpHZl7LV) zX$v2yquc{&c9dXA4Uk6IXmP8L=$*(MyP&AihZ^D6zu3_R{e=R?eo&(G zgA&1i|9A5rl>F<&q)_1>d>FMGiksGIAa&&UH3jzB36t8@&K8KuOPGl~Sdzxq8MLok zG>?S8p?u(Vy!;k|@2}?>b17=?6)Ue>Yv6hw&-f2<^6QYo2k0O#M4vuP>vh?m3~FAs zWF|jlFeAtn3PM((0JAqP$ndl)Z#OhZ5y~7=^E}9~1p_iy!7Z70a`oMBSE#o}pjLJh zVTz*5IIgH$C%LtC9E*RfOV079G@4(p_z1lzvA&$?%4XRKRqv;AP-^Pnu?;u+((h8i zL2LgIFjx6Cw&tN3x_U7nKUtE$c!a$9$#6D#qZGn;&uoa&U&%^Lp(&%yiJeB8xx|}Y z`tgF8XP6d)@q^wa%SeIAAnL0Rk7uuKv@%S~4y(V+fD5CQP@ZZivy)%ess1v}K?`t@ zQuF)fi}JY6u72#6vftxICFm+nwzg$GCg1zMT?(U0_l)Pc5!=B4LxEJS4ns<{gO;!< zXgw`8Hc(F_hbG98bMbG9=a+QL9r8@r^6nI{s-;H15v2MGagO#T9zUH9Ae$D7YdLjA z+b+6rUT1u5x61&npD`pu?-5155E}FMJ^B~@Z|iSJ|IA;1n~6ymKz||ax)GgDo`@H! z=P1HkG53^qWlx#xF?6NhQERNoVoC3Pkt;yj{nM9isXV40D1&?jp+)C!d0N7Z~W~jmsBwN~D`fatRBJZO#*%k>!yjFS^0uKVbnUJd2Ryq$#3wPIxJfZVqJ{k&L&9 zXGCBQb4AEn#6de{voh66ZgSnUtK&f&3VPU`{pLb@%fxrO3nm!q)B}6PdXBGvSNwRb znYu@N!ldSa(*GSjg59@YnmN^50&QLU~Q;g};bg&FW1uN-D6+(tiSj13|*jaU7szS?JO%dg{la; zsYTbJ>S51)l`=Ja293O0qU*grE{>~Vl~KEju8(CD)=RK6c8wXv=Ry{0eQY>gXHbMs zf(9?Q^CXoZo16h3k5t4ol0WgU@(59J#$rXL#!T$oiR2;)m5l~P=ou9rBG zKW3L*?Z8_lpgc$u*MB}N{M3p2H4S>dtnu8Y?ig969?)uZXiMBkgy{rwyvHX{IwQ*1 zAaq*bEdCiNur{67aksM~O|G6rDQ9Zva~!a|*~U!cX7%1NuGu&KR{sIq?_r_$D%$FK zxv_K6f~%Io%g_V7`)TPMKhqWVq~k!XKec!HEiArL`92$v=|=Fy{>{a`u^4b%_X}@F zaX=)3VSRhobHA_OLU51xa|m;}5)1(E>KAu5Af;kUL_1Q|j#ePnvNgw%f9VT`kTto~ zH}bUvD8g--TZr)D%6`~)z-4bH@U}GFb+C$o1;du}!_&pT=wTNZRcmcOcPPeBVAB6U zApYkL{b%<4&!DbQ;Zh1g7M80S$3itpF5HI{9ABip!2*Jmd?dIe6pq(l?`GSuohd_}1NBcI-LaLWPNMI*u862C=;tK_$ z(n&p`Ly#LKfE1kWXOo8=oF9Zma{O61Y#!*hdweURwIrF`@}}l=L)N;UYbO*a0={5B zQUPPZEY(0o5Osk`nMW4tB5m+6q$f&l_QhIa+@Wd8uwM`_ByCMc5C*DD%?Pb~C@-qq zcUh(7rHYZwlq0;NNurHgAibV_8IBFj&GvdPGrx4aFyXuJ79qf40_xr5Z*&bu?vUHi zrL{iT&VA80Zh;VY{H%tC6_8BZ({o_1Zv)FXq{4b}9w7xB9s!AIEI+J~1?*I0z!gqC z3xG=tIMJp6tvi@N)02M3zh-%m@oA)pc$rU1H2dNhDf8U~Nl`etmlVKWe5;&7d?}X) z#txXgpFv;o;ZgP|?+G}GT#aCqPZCeLfh~{RR&(0C1`nBj>JD@+Yd*Zipb_W7Gf&dR z5V2ZWykWs2WOT2WZg=R5kzfX%oX!y=y@3yCsa3&v#Q~(KRS0=IQG@~}1gL_Hi9MPT zOb$ZvS{D{a8pi$b?0yjmst@Cz0w#;kwov4k0bZp8{{js0aEg`EA7HHgs5Ad#3jY5h z$|y+wcqmZ4jM^{z+5*F5kf?I-8xU8MX!ONG3S{RC{6wKbw}R+RQPww&oWsAMXvhap zt+d>3e}@taRsYzaJdD+4Db3PcR$O_GT)VSUS82Aly#Lhr7-D^DHL6>UFAa!(Z`tDH2S}%#z)&5j#_v zI%kw=H*yBO2=zB(wjZ=7X^wI{0z0=}w?GQ@HU*|v+fE|{v@1JogpFc!`~(7k&3Q|dsgmZW#r!!e8PcYLjUy34;4uRDf z9#U%h>|eU(4V1H2NwYq^1oLj0j2<77JiF#IyodH-sB`399Jg_m`T>J$i9NBqF_T2| zyC&(TTyrJmb{i;KT(J-dQ+S^>oT@Y3lhjgdc2vlbcOEcq*0q?A*6wQ_9vQ>{0LuDb zZRZ6M1wCSOOxa5#T1c;C9jdqIy%R@%1LB=aqoVR=;61$~LOOqq4|2q|NfP$om`cza zxN$MGnK9`qf0*4Mo_0+=CIO(it+Jy|&3OL}#D@u}0H~9Qi!g9G0v+R!Lxh||kCi%P z(<{KR{57SQLKrXLIm6Z6l& zc$4!0Kzl;r(d}r&AQ6n@8xKsH{QdVC#Q%mnNLtVTh4tKLwY8B;`=gfQktp{QX3*lp z`jUi_(Lx+oeZBQoN2=!c z*Zn<;PjN}Bi2kG?u(|4nb8Qp|G&Vaa0zF69U4C+aLaW{18t48hLP};2qUR{TriE(( z_nufef{Tz|-WBOp)YCQ zAo-a9Tr1n4nZc&V?(4X#(kb*jw}?4Yd6IXU`Uo~-tv&3WlZt7X=AE&j>pXna8_WF7 zu%l%hY6M+wzY%r-KGIFb{7Rh~U65B(_(#e9GL)8hnJqlywnCmU+XCwELaE~6}7dR^0< zmG6o(Pe~FJK>Sp-LmmQ_Y{Ny|<%<-BV3k!?K4k7SP4Ui}8v#G&m)pT5%^uHxV*AOf5Z3mFX_%v@} zNJoU0h@y`^L0CQPfmGf{+kDXi6rb#B zHBK+?u?~L}H9l@Q&SWpRuHhg?M142jRAWZ!52aHNiFbvJ8aIyf!pst`fjGf5-6-f= zwb!bz9W=``d@FkoH4BPMZw#@XZv2wK9l1@uAviWs!4QCw$(cAyCaF|bC^_yq$P%7Z zu{nCX$L?(D3Z0;9JzjM5)QOA}SWlpp#I+9B9jRNo7%=6RC*+7oc@0!e*%D|r3Xd&G zl(~xANHEg(s8pe8%^PLPo!Pq5z$A2(dTpf|bb^>)2{CN|a^v@|NwKqqt4y zZJw|xD>_7omTcgs+u=xRHk>B!XurguZl!#dFd1?Y8D;e#LZ6?H0EVS0ayB!QtN-g$ zcH%6hKcDnOkn3A`eE6n7uz(m=Q__Lq7zgQdsbNhgsPy3#m~(CooW9}SsSp8C3pFuJO|^k466PtsDJwZU4jVD^=Zf6c$sz zJx3=tMkj&d{`&C7jN}vI;f;uc?!x`X7yFG4w_mUx-5YG#Gg~Rqd!M6RXb^Pvi z%t2y}>Hezt%l@$N_n%u|v#*jgp3)OuAYCVJJ)n-Lh+21Y{5( z{EQ?{{yV5!#4u$K;;=zlSwb&nd8J2pr6J!ak^wTk~#7Pug_Ji~W zzIeweDy5|82Dy0Q5*14Ejdd$Dj$?r03lnnPl=5km%95RA6a~DGO6YZEuqdOgUaFQO zu4U~)q1@XvD5O}+Z-ug-R`dp$p%jSwk9xHvD07!%0Tc#7cqp%hs;f4&p-QVcZpkl( z`ElaX+Gb+m8b%|Bzs)6CF9b07oG6b5{^&0|4*JL1*mI&oIx`Bew_lWCMGHW+^3k^T zMzNXq(UD+64Ee8TSm5)lC^r`p9Ug|pAbz()b%^tO2IYYLF!PBtzZWsd% zvISKmColu+(}g)1pXXz_g*7c$hjGX{Ga7|Zq2>!uK?&*K9$hJ&Et&?ekLm>0lfgUI z4MCYovgLTSV>!|vG=YIL0FMldJtyfX3?Oyt8JihgBD<$+&SSv@nW0}+4f^>V=?Jex zISZFs+aFnEzB3pEbC_uWhcEv`H8VLSZ#J!#o;EbI?WSGIwwI5GE;R)DF@be11NTRj zkL(pD$XEpP#a>4CVoAC8AxU(M|H*%J8Pc*TD%d;?W4CO2VlbT3e26X=rIpJMW)||t zBtD;=S4a_foJ;IY*+jQH0n*l_#f+dqI!IR5z`tP>Si>@8Uo<S{B0)7%2v-7I!k$kBpHTmCx3?f$ z-V45|wQlS}4y_x{$ax0I*8%XXm3rf9hzemc%s^*5MWkUflo)UxE7I_{PCY`gk8D7? zq}n;5q%8X6nvMkAp|ztEy>0Vq?p3_-m<;NH90_JLIdb`iwJGs})O^2~OaVug9$s;( z1TZ#2rV}R?B2&11e18F2sxI5*ZBPkV_iN@8bnk)$Oa^XTk>TskAA@lF)Y$Wlk=8bD z^~8Br&7r7Oww1+Qove3QT|**)gcG2hqNcwNmx zdKav4mfpGzC$czs#!CmON)5DFpNkY2Zp|nDF;s7?)6KX+izo--brmr3100TkLCV3NKFgNP zzRDHL-TM{8UGWvFl$e9gDvqs1tm7e8r(%k}m`Y@=_?SSB!g#1F`AJPqV30|!=_t#h z(Fz>96BCh@xDW?bmtWDKMo`x_sQAIHQw8-0=%M6^dS$u~RhUPwsr4pG9c@snMx#!v zz4g;^nRb;#+41L~7pu1BqmOog{Kai+aTtfhd#kjHA~ZLN2kB_bi;KzHjR#|?NgMbq zDtE4{hNCD4;Yl8%E#gLcPNNlK;#P_4h`pCd8+gw2kPiuIy;x?#P+wJDc1lF@JeRB@ z$Q|W*vmy&|?Fno9LHPW%3srylO;$JUqKUMV+^Jr}>;^sS*5lp}0mQKrIH+7jfcj1_ zg+s$)`O(~+Z5M1?oCRX%$?t%xb;lIl73z~;%t!lwX8%D0z6e`q4aN9(@%@&dO|W@V z;++@g`9#rU`e;?9(L$G*XN(8Bx}*DJ_pXYD$X;RIbq8Rr%D=?B$lobn(>RSrmZ>`M z-l<&a!zIsh8VZC13ys|@+*k?NH}m`AtVbM^IEkd?ryM$Cw+$2q#>N(Yi)YDlurNR8 z>WtKfeX;c>G{i;QZ0iQAs5v{=VT)>lsdThblcv*gG3QgFQq=PcL_cL3UQ$N(Nxf4R z4mK|YaaoT7B+@rRIk94fCa+#z8pbv>GA{?k6IfD9Qd$Y`8?O7`P8u?l8Bd@O1+~5F zk3b}KkS^EVpdSt0anCSL5RrJwt8hsKk+@l)dZiqBrNB~tHz-%_@?V2tbD~Rua0hn; zWoW$_b;r;ONq=)Qf5hY79~#b-t;BQ{x$wsnqi}_51Z!v z?L4$6bsRH{)NG@|>9RUTPPU;ONhxDMcV4ew6>^FOq?dPAiRxB-ce;+K97R*jDvO87 z%8ORzfSUXc=Fjj9(@u|Z<>=g^{8`_qMa2JjSc)TIdA9;7Ovs|WIF^2?5?@bHmEE9n z?$-A4c@Mu-|KO#O;O7Z`a9q zxJ`0HDXm>7us3bPC>`CLNegu8cx_I)SX5V?5VP5TcLnIIvESG{2TtKQ!ND(1UekCl zc7Z~|Rf=E8iPbjA*?%a-$`REL@!^e6s)e9S6@+6`78Q&|uy3@IdM-hfL5b}12!>@7 zfi4+{dXzwG`c-9RA($`Q=dT2GyitLcY8XS@vZwkO3Ci+XqErPHx&*hRQ>k!PAe-D( zKu_wUU(Mob>8;nnjzNB<#*tzzfAQ<1dwkKY{0Grhe`2(zv-PHPL9cVv!zUYJW6qGB=2E|tUuu!j*P^h z6A5wz`(>$mvRL93>J%R=#xIxH;;J2358v*)8^Nzz=BoGRGwaZ{3P8dA#muN~;kYDc z>n7*>Wq6krKp{owp7p!m9-g#sJ3KjP8~sZMC@ntYOMBxNs?=;(gUT<86<6XlZGIJq zmjh$mh%uR~bHRQ7BgV^SsjIB;v!HL`s&hF=eEGq3m?O6obVrt*UTHzU@Z4X z-?+ybh4+k#yoVF~sH@?!)5R-q4Q|Rswd5kTiVN*bX#f!fWUUvZ%G_8Wh_-8~Krz1T{UZn5L6|icUfS5@Q;jk& zVuJ-%WbUU5U_BeB_uF?JDo7x^y#3+W2V|U%!@mnHH_HruYy(upytxuSII3PphBQALx?9`yvjWq z!{rDyhWNr%9n&I}DeE;wT&`j5^IrP1xa2A;y)KY>>7rzO`p2Zq`2~9mCr27&C9Y}$ zfx-Fm65aMd-EO3PxIP63dL05*oaG(80iFDGhV@zm4jY1XbsMVt3-+Lk$CYS|8+hS& z8-%Yo2Jc~sPn4sx_K6vo)bL^3@`#>GdT8enLM_X2n`ng{EjEy6QHHDJ@!K4W-u}5j z;R82L;^tjjS9s~0wa*aDf%rR1PNM34(^t5xCC6U85Qv z#9;JkXR1$G`yyCjQMyIG)@UwUJ-!4f);oc9t_(w1yln2mwLz7>DA6+c{VHy#uD;PW zN?W=wE0W_bC`8(N-?(lFJxtjI;7k!>)4VR^AiV>FUDtB2%X2l;BD&j^t*Qr5y0^;) zw?b0Lo~#FTBRnG3aNY;OfGPz$bxA(;DSs7~`8HJMf(s=V$pp@Z>o_eid+dOnJS&Ua za40~9C)`k?Zi>!KS8xnaf9n^g-+oHVESv4eYS(du>_~|A515P|J4yDM=;2 zM0UyQN$}xOR(jHhN`2J1+j$tsogdDId=a1G34kCCB(G4k&=$@;>O>I|B>>^{_48Sc zF7goM;qdlV<~?UOte=}I&Ji_tE;=J>U=Zsh&qu-Rdjs0a+UHRgr^ak6plCe6KMeF@ zJU>)>K~p3`ao6e%LWVNsOi6dIjRmGE6I-(kifp$A3{Sw{=m9-@#~)7C{Vyvh&i?kDsRp06ZX^m-c+W=jeJ^p~r` z&+tq(N2?f3FuG>)h|bl(t=@I?$kxS)Nd|=ilsIL(qm|b|;aqq@BJM+w07*Q$e{p1b zO-~@UruWqZ<2gtf-?x_M^b)WpXI+Vm9hQZ_$sO<6#&`h%{5IL4!UqK9F4uw1q`lGK z{0=2%_apif(a-9CV}ppmK!6k0&h0_%`)R_3$Lf)y<^B~YGbDr6N0;I?p&eL8ihQ+5`uJtvS zwQtSfbOCxj}B3QIBrNu;DxC)>e6{U)~!hCzoqNp zny3{~n|&&G;_;E;K01dODI8 zgce24dlcM~M_7Q@}Ut2iC8q15dzD=iGf1Qb}_RWK_mU~xGb!Gi?!VX_-6|Lq=cFf7%4eVe=NU9K=Wtel9tQbDhyk7@)G zaj0%HnuKM}X@kYq@wq8P8UR1P)|Y09o!s#I`tXB|@NbghgAV!lkM0-Gs6jjMIJD5~ zLTaM>2S^zW_=`bgY{)EZmpg5NLtngzEc@%fOLn^h?{04}l=FyNQF^+-l}ln;N$hmK zs2B#P%)WyHu$muQ{niPwIQuM9iJKo*_bCE-xZ`Z`Ay@{x264);+4~-3-OIP`T-_`# zcPeW@wg{)zN6*M}nuJ;(iPbyb|6*;C%?G9x{IRt_{!DECkKr)?_lU;ef7!wRXIhh~ z{OXLMjPxZGE}TT-R6%H#QB;~Xm}EFe9!XYu$?iDUVr#}hM9pkPMw>)@R}d$J6`8?0 zlQf6iR@+cvy2>IC8e=EIH=_Fr1?>&keJd>^B{lK96=5)r-aH_DJkfsL)$Vn@#gXs5 z^)|2l3$yQ#bdR)*R1ofOEmCKVLP9=hd%Cg0imbqfWFZuEnWf4A+bwIgp6Fm8DZ5NW z9#*z_|FNv%tp!F_|2^DKvo?fmnI~PCrHkyKxU54iYVWw-r`#WH1%;I6#AaySpFu+JAajI9B6z9S6suF{--a*iU!GEB`hCyV+7663v!t`g(2DAf^( zvqL8QNtR_6sWrH?nM7C`d^aC+_^@#|yt$va@g@GW)5eal`&80|=ud zy3H!oR{ftWnPfWzqfu6(PngIVY4=rTa-mUM)x;s0BB)^ecXT%Ht3tf}4*m0dr!KVu zHuSYNA8)lLcAv_i3|cY6Gmlf87vpW zgQK60L2h^GY9g%N=dM-xTG!K_Ac~xyX35Q)Ff>57LNZBXOgcjz2f@}X4z`BsMOa+#jN$U=Mv3JwNnzIQSVcM;*Z3^E zA{w3pwPu#}T&w5q>C*~S!>Ck;QfkE4_@~-}UTIWF({*R?NVbKF#Tt%?4oqa2m1%() zy5ShK6#7M)xe0fFu-=Hz<HZzOA9QOVm*w#3~(}3Db$((Bg$sXXoT3D=1ov zkfK!s{bCbgA!eie60>QMBl$du2R;Ll3Orz#P0szlxIga=FiAe;RxOO3j-ZZT+Q5*? z6Q|eE7B>era5Jggs7a`%P6Eqn0q!c6Z}Qx?#9q-qP&^E*n=zQ71Rd7O)>QQ;5D{>< z2$yN_=V^VeVH*_*rA`uoo|=OY-_oF8)MjR)Bm6AOLGqg_X~2FldHi{{#Wi`MrnVzD zalyDY`H#%&obRVPCEA+Q3Z{==JPNl2U5QKkReQteUVho+E$bNh{-J=04tckZ#4b={ z#YfY19!wIu2|?Mr#~!MdwAhG$=D?u3d+3Y#ql3UC%v@ma(Y->Q6+guK5nSZ@t8GPl zx0v*OK4X_58bPD7r_r&0b8Ke7bAga^g~lBc+6|!@rJbWB4|#ay?>4(A_g~*E1n;i@ zK}pYZg7p5CMF#s2%bg+NMygbkP)>)A8rmWDUoh6^L%h% zUUA?NX=0>Bf2xpSkG+4hsathn7-sQHVo1_lFx>~p=JvevkF4kt|1(jzakgQep^wom zfv;MAa8fkl6)X+?yXVr&KOyuO2y@d*%*(WiWs2?0ULdr`zIB!l;Q2S1<20 z7k5(g7f7pd_44zx-869ZHB4^e`7ds-q;y|P;N;>sldO2o=P!Jawe8~XL`#|I-*kidTo?f;>AJ5z^yPW zL_Yy?tCFf_94%n=(yi!hm6D8JwG0Jd^AsX>tTdbR>88;CQdLJ z+Iljw44H!snRV~hZ+`*L@|C{R2I#7>_C4}O(DEM*Z}R&T2-zmMU=mc?Isr*%;l2Z6E@GdQXQ zE6yFGUdVB+48dw^#eF9P@tRto9xXw7caarv>W81sy`xkBCuxLSS zJYB2+XzL$#8wSySDztc86VU-1jzEqUjNycoV#A3LHku%J`m6DjMA&sBA%70|xj?F> z$%deE3^iWo4K}dQJT1D^^_tdz*`(?FuPq%TL5j8}E2Sgk6A=q77Ds1ZK30w{YP>p& z#8Vq#UY6HzAXjm1xJI4Cl-el^%?p2>fy%Q1LhYK1u%WXGg+sMSOM7{D<9fHu zb+yr%#^ebn7uVIY#S~TK9&<jqK}aJc*IBTk3GesKj0%hEbwuH<+{l)@|rc5 z-GAQ-{>shxYk_GNTO?bgUxJQ-v*(hd_CtaB7b_}5`75XJCbf7RdWO2IB<%VdjUhYJ z7abavE%-q)IMZ(_rXmIk8F0$b2D^fJ^0L!SFQ5mNFGF1!vnRa4I-tx|iXn0K<@piu zn!I_Zc>>#8+J`5P%s$me=Di=Bw0FgqGs=|<>MNzw1bHV!z{tO=ts#3LXvR1i7b-bB z(+XTuNJdAmk#H8ahCAUo5Qv$Z{fbN`t@EL+^l`ZQC3gjy8wnWDjeoZ~-X)RmQva6+ zAGHTbjm(R?DsQ^~dbshIIZMyjaTi`&a1+4*v%>4I+w4}F5KMetKAu0j2ezypAqt?~ zIT!PzHOjTgtiStX=)^XLORSQ-T8qwJbKZV^5`a2_Gx?9e%J=f;XO4t{e|#d~(b1GJ z^$Gx@Zl~deLFp61-Us0Gwc!6HhMq<4J6Dn~itURCUOqntcF|)BJI97<8wc2{_enZy zpQYA?u{$78y*U+Vo3?EV&0iyA3X^e@^)cYW-}n9(1BqMq&0Wxs1(oS1R!Zdmh#os@ zGedoc|34|qg>mCjeSZ;yrfpDU|J?f7%CZ25%mj+lgz{;?5%t#KjMYM#a!k_dxKL=O zw%h=CknWQy=-0?1w6l62Uw>z^%}<=K-$VSu?AJn;lNsw#0&Zfci4WRjOh7A;3M6@8 z^LHs+(~mJ31E3#i4h&vKXpTNhdd9K~voy6W9!>;Z%1xc&r!$%{6E{rXI9`I4OqQNy zxJG*RRQSJ2I}>;)w>OSYhR9M~LZos{lo*6aQd!12G`6~;m}DQuPLfa|WlLRKT+1|B zveXroREliLTFIIgd*oJ1uD}18D_+jkpnH6Ltk3UzmiN5pJ?FgVd8qGL{!Dwzg4I zc39+X9C0Lx{^I$>^PQTBw{Rf3>3_1Om{>t(y9z0b^~)7bDnHXYu{`Eble#U_&d!&& zqO0muWxsKCv7awPsWYwfe3b6hW)i9BW@9*n&ud8*nVdYs9=}KKc5lSZ*Y`aF(3%ap zE0P%VUey^Lu(i4%-Ej2%ie^l4si4mG?ef)m+S?0RB6Dg+JSu{nl}^7YYktIO@2mXg zk6v{~eslFzn0gh)_}|ncga~)ueQfGhocpp+;sA$J2xw~&(AF9YwKW`wbJkP_az%>tbe^WB+J|Mg2}58P`%3hV|#z$|=ikYS{X?2i_aoWVRqrw4GpRmSYS!x-AdZqF1dN@&?yW(6tB{}(slgRUw^dojogkv5-xylMbrrR#(P?LBG6U_1d zQ-8r#_esbnGGsqz-4h|7i~gBpB{xT3sAEf?O&#b5@0H&NPIZ((W9#CKl(AZR>XME` zPb()$5P(&J=uEVS-MZpoOfkqk;1$&rj&6sb^2G1b7ka?Ij}Axx}kXn%#&Ka~=( zBEvbvGPh3#IS#_E#a-6As2n2Z8TwkqN*zO|#2W&)1eLqCc(ck-Ndj;4+eDMHIV!@E z2`}z$+Q+u8`;uvWxbY`D(P8UE-9Rw>pa4WEPe**>A*Ffc}-k zi2sj41}83Yj_aGWadB=UoS))DMxUQ;iFq7o#;?R<_pkho;(Z-2L8j8P^u^D%f+dPG;UpB}sTa&=$IoCtP3saye==&j8<*KzwMwDHF+b<+pKzqR{Y_P<(F0mwn zrcl;zL6KVauEe4gHDhPT>Z@l>wLeSVa>1q*r+G8fesLU+(e^7VMd_Za%hk|*$~GF3 zn(%p#^~OgrCASlWg73E2-_vMibv(SI?cLZI?rTqZtAZ%clOC0It!$JlW0yQ1n#S!g z*z@YiP5%vnB#(n^Cz#oLcZFs+q^eM3S-;B$08#&rD;RZ<<^bHMtZmD^iqw zuBB65e^pB8LmvG%aninJoT`EGDyKd=Wa&3AYvQlr4>f1xEy1lR(5T+zoBBF2uU+0g zDv*2a$^5ln%`9J`F_)uF_lEA&znh=2`?0e2I!uhX68b>eF0xOMaUf^1X~ue9sF|S;^NedDo+GnDO%C+Gy1zg=|O+5EmS8KfwBxOGp^YhWZl9LB+ zoWXCn6}9=cTl!D|ka`B=OG1C=u5GOp{kS!4e_KL!?fWQ3@Ge#H@5XwH z8|@}}^H&;Lh*`Eq-rHN*GBln$7*!&cCq~X4tGQ10-EhUmc2~V$442}#p4}EhN{}hO zt)h1`@j%<93zx6DSiUeHVsA)enh?3KU(twm7ct2hzoFi8Fhz4PBbR4oFYZ&Q$;dT> z!C3D0%&p~^eRAO~HLXDdSN+63B{Q}9X>L4NT6^*ZUtz>@ANBO)j_s3mRYP4t;v;y1 z1J$k76io@2(v=)lQ}ui_yf*ydMmBj?=0@)9wY8RMTQft)j}b1B_xu07p-@NTt1O1- zrP&glb2U2-`-Q`(;a+19I#@FcwNEcG3AfmuF+c=pxVoPID8#uB=m8}g~n(O(fV>{k-yrT z%?ghWQ)IKh$vXwJZ@YAD40G=ap`+1KK4p)Br_1Woavo@T^m<>PC&B#hU!|J&ey|k_ z4nD3pDDgS3(P11-Y$uQNhZVz5N6F>F!h6BZllEk!_MdK|&aPx|cXhY3a?=stT8Y=e zON`*J*XWAt)HGrxwZ*q+Vqa@ZR!L$}q20V!284MwiP%v31Gsxj)?B>8!)?>u^OApn zubibAoVP(51dG%rOn3B)1%o>rsY(~gcHxBV%zHNcGJAG5LXzusqp zf6xIB1mL$bi4w3Gd_OZ<=ql@JspAZdBy`p3fx$rYJ<-5uph=7HP0s?jFr8%~{M}+| zNTO>9R$pfs>diHr8rccBgeCIxUk5pYDmyHW0xgInO29$zSUV$u*HXpl8RB4To$Jl) z{=g^)d?NLZLQw)fbI!8X+h+vqVdLNM)J_c802p356&!dPP6 zCE7UwrwB-(Cm67|{rYWDP!Y8AfYQ_I;43A7XB{1Ynw2%tgXFFTJT;NX#G{D6V^}|d zVDJD7^jm?x;T-)4a6Qv{?DzgRb=^((gMaJ8lLIg#^ggES;cg28O4wNB&wi4wpM0>1vR)_@;4cOr@Ob#+|3e&Q7EJv(^^|?+hTO*&u!_h2Ss`y zx5A)}f$&VC1c<8AQN@#OY^LLn!S!0&Q*9~*T1_5YgpxCYw2a=t(UH`pO*9TnO)F@Z z{`~n3`;;u525tv@p!e>cBQ9@1N1Q-(w^ep?vvNE_t6@CZl1Ngs1HH`dhzAnP1TKgR z&x+=ipcT78VZ`UK6Yo4@10Zu1dFQ^1lLKX#%I7Y+9FjbP)?{2X?wBENh6hH0t!iov~!_g0%`C9z|%z*OpA9f0PuiVfdgO zf~Mpy6+QnL1HT-G5DZEdApC1jdVT`D&y5iJDway1HzLD3f(U2xlZ7~o-yeiq2;Q4Q zs9aAMpu!K)v!10Ec)Wr4NDwHhZq{nR)NJ^N3n_D#JihOkz~zHi5)l;c*?&PH>xu*& VCNKd3JGtOvEm(5t0lFyE{{i--k}m)N diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index d83cf136..423c23e5 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,16 +1,3 @@ -# Copyright 2013-2023 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar \ No newline at end of file +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://maven.aliyun.com/repository/public/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..bd8896bf --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..5761d948 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" From dd2ec8fc39ebb5dcdcb6ddee7c7d53ac17e86f92 Mon Sep 17 00:00:00 2001 From: Mercy Ma Date: Thu, 26 Mar 2026 21:03:09 +0800 Subject: [PATCH 298/310] Add wiki docs generator and publish workflow Add a Python script (.github/scripts/generate-wiki-docs.py) that parses Java source files and Javadoc to generate Markdown wiki pages (per-component pages, Home.md and _Sidebar.md) including examples, version info and API summaries. Add a GitHub Actions workflow (.github/workflows/wiki-publish.yml) to run the generator on push or manual dispatch, copy the output into the repository's wiki, and commit/push updates. This enables automated generation and publication of project API documentation to the GitHub wiki. --- .github/scripts/generate-wiki-docs.py | 831 ++++++++++++++++++++++++++ .github/workflows/wiki-publish.yml | 79 +++ 2 files changed, 910 insertions(+) create mode 100755 .github/scripts/generate-wiki-docs.py create mode 100644 .github/workflows/wiki-publish.yml diff --git a/.github/scripts/generate-wiki-docs.py b/.github/scripts/generate-wiki-docs.py new file mode 100755 index 00000000..754d413c --- /dev/null +++ b/.github/scripts/generate-wiki-docs.py @@ -0,0 +1,831 @@ +#!/usr/bin/env python3 +""" +Microsphere Java Wiki Documentation Generator + +Parses Java source files in the project and generates +Markdown wiki pages for each public Java component (class, interface, enum, annotation). + +Each wiki page includes: +- Detailed explanation of the component +- Example code extracted from Javadoc +- Version compatibility information +- Since version metadata + +Generated pages are written to a specified output directory, one page per component, +ready to be pushed to the GitHub wiki repository. +""" + +import os +import re +import sys +import argparse +from collections import OrderedDict + +# ────────────────────────────────────────────── +# Constants +# ────────────────────────────────────────────── + +PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +# Source directory path suffix +SRC_MAIN_JAVA = os.path.join("src", "main", "java") + + +def _discover_modules(project_root): + """Discover module directories that contain Java sources.""" + modules = [] + for entry in sorted(os.listdir(project_root)): + entry_path = os.path.join(project_root, entry) + if os.path.isdir(entry_path) and os.path.isdir(os.path.join(entry_path, SRC_MAIN_JAVA)): + modules.append(entry) + return modules + + +def _read_java_versions(project_root): + """Read Java versions from the CI workflow matrix configuration.""" + workflow_path = os.path.join(project_root, '.github', 'workflows', 'maven-build.yml') + with open(workflow_path, 'r', encoding='utf-8') as f: + content = f.read() + match = re.search(r'matrix:\s*\n\s*java:\s*\[([^\]]+)\]', content) + if match: + return [v.strip().strip("'\"") for v in match.group(1).split(',')] + print("WARNING: Could not parse Java versions from matrix in maven-build.yml", file=sys.stderr) + return [] + + +def _read_pom_revision(project_root): + """Read the 'revision' property from the root pom.xml.""" + pom_path = os.path.join(project_root, 'pom.xml') + with open(pom_path, 'r', encoding='utf-8') as f: + content = f.read() + match = re.search(r'([^<]+)', content) + if match: + return match.group(1).strip() + print("WARNING: Could not find property in pom.xml", file=sys.stderr) + return "" + + +def _read_pom_artifact_id(project_root): + """Read the project artifactId from the root pom.xml (outside the block).""" + pom_path = os.path.join(project_root, 'pom.xml') + with open(pom_path, 'r', encoding='utf-8') as f: + content = f.read() + no_parent = re.sub(r'.*?', '', content, flags=re.DOTALL) + match = re.search(r'([^<]+)', no_parent) + if match: + return match.group(1).strip() + print("WARNING: Could not find in pom.xml", file=sys.stderr) + return "" + + +def _read_readme_title(project_root): + """Read the top-level heading from README.md.""" + readme_path = os.path.join(project_root, 'README.md') + with open(readme_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line.startswith('# '): + return line[2:].strip() + print("WARNING: Could not find a title heading in README.md", file=sys.stderr) + return "" + + +MODULES = _discover_modules(PROJECT_ROOT) +JAVA_VERSIONS = _read_java_versions(PROJECT_ROOT) +PROJECT_VERSION = _read_pom_revision(PROJECT_ROOT) +ARTIFACT_ID = _read_pom_artifact_id(PROJECT_ROOT) +PROJECT_TITLE = _read_readme_title(PROJECT_ROOT) + +# Regex patterns +CLASS_DECL_RE = re.compile( + r'^(?:public\s+)?(?:abstract\s+)?(?:final\s+)?' + r'(?:(?:class|interface|enum|@interface)\s+)' + r'(\w+)' + r'(?:<[^{]*>)?' + r'(?:\s+extends\s+[\w.<>, ]+)?' + r'(?:\s+implements\s+[\w.<>, ]+)?', + re.MULTILINE, +) + +PACKAGE_RE = re.compile(r'^\s*package\s+([\w.]+)\s*;', re.MULTILINE) + +JAVADOC_BLOCK_RE = re.compile(r'/\*\*(.*?)\*/', re.DOTALL) + +SINCE_TAG_RE = re.compile(r'@since\s+(.+?)(?:\n|\r|$)') +AUTHOR_TAG_RE = re.compile(r'@author\s+(.+?)(?:\n|\r|$)') +SEE_TAG_RE = re.compile(r'@see\s+(.+?)(?:\n|\r|$)') +PARAM_TAG_RE = re.compile(r'@param\s+(\S+)\s+(.*?)(?=@\w|\Z)', re.DOTALL) + +# Matches @Since annotation on a class (not inside Javadoc) +SINCE_ANNOTATION_RE = re.compile(r'@Since\s*\(\s*(?:value\s*=\s*)?["\']([^"\']+)["\']\s*\)') + +# Code example blocks in Javadoc +CODE_EXAMPLE_RE = re.compile(r'

\s*\{@code\s*(.*?)\}
', re.DOTALL) +CODE_EXAMPLE_ALT_RE = re.compile(r'
\s*(.*?)
', re.DOTALL) + +# HTML tags used in Javadoc +LINK_TAG_RE = re.compile(r'\{@link\s+([^}]+)\}') +CODE_TAG_RE = re.compile(r'\{@code\s+([^}]+)\}') +LINKPLAIN_TAG_RE = re.compile(r'\{@linkplain\s+([^}]+)\}') +VALUE_TAG_RE = re.compile(r'\{@value\s+([^}]+)\}') + +# Method/field signatures +PUBLIC_METHOD_RE = re.compile( + r'(?:/\*\*(.*?)\*/\s*)?' + r'(?:@\w+(?:\([^)]*\))?\s*)*' + r'(public\s+(?:static\s+)?(?:final\s+)?(?:synchronized\s+)?' + r'(?:<[^>]+>\s+)?' + r'\S+\s+' # return type + r'(\w+)\s*' # method name + r'\([^)]*\))', # parameters + re.DOTALL, +) + + +# ────────────────────────────────────────────── +# Javadoc Parsing Utilities +# ────────────────────────────────────────────── + +def clean_javadoc_line(line): + """Remove leading whitespace, asterisks, and extra spaces from a Javadoc line.""" + line = line.strip() + if line.startswith('*'): + line = line[1:] + if line.startswith(' '): + line = line[1:] + return line + + +def parse_javadoc(javadoc_text): + """Parse a Javadoc comment block into structured components.""" + if not javadoc_text: + return { + "description": "", + "since": "", + "author": "", + "see": [], + "params": [], + "examples": [], + } + + lines = javadoc_text.split('\n') + cleaned_lines = [clean_javadoc_line(line) for line in lines] + full_text = '\n'.join(cleaned_lines) + + # Extract tags + since_match = SINCE_TAG_RE.search(full_text) + since = since_match.group(1).strip() if since_match else "" + + author_match = AUTHOR_TAG_RE.search(full_text) + author = author_match.group(1).strip() if author_match else "" + # Clean HTML from author + author = re.sub(r'<[^>]+>', '', author).strip() + + see_matches = SEE_TAG_RE.findall(full_text) + see_refs = [s.strip() for s in see_matches] + + # Extract description (text before any @tag) + desc_lines = [] + for line in cleaned_lines: + stripped = line.strip() + if stripped.startswith('@'): + break + desc_lines.append(line) + description = '\n'.join(desc_lines).strip() + + # Extract code examples + examples = [] + for match in CODE_EXAMPLE_RE.finditer(javadoc_text): + code = match.group(1).strip() + # Clean Javadoc asterisks from code lines + code_lines = code.split('\n') + cleaned_code = '\n'.join(clean_javadoc_line(l) for l in code_lines) + examples.append(cleaned_code.strip()) + + if not examples: + for match in CODE_EXAMPLE_ALT_RE.finditer(javadoc_text): + code = match.group(1).strip() + if '{@code' not in code and len(code) > 10: + code_lines = code.split('\n') + cleaned_code = '\n'.join(clean_javadoc_line(l) for l in code_lines) + examples.append(cleaned_code.strip()) + + return { + "description": description, + "since": since, + "author": author, + "see": see_refs, + "params": [], + "examples": examples, + } + + +def convert_javadoc_to_markdown(text): + """Convert Javadoc HTML/tags to Markdown.""" + if not text: + return "" + + # Convert {@link ...} to `...` + text = LINK_TAG_RE.sub(r'`\1`', text) + text = LINKPLAIN_TAG_RE.sub(r'`\1`', text) + text = CODE_TAG_RE.sub(r'`\1`', text) + text = VALUE_TAG_RE.sub(r'`\1`', text) + + # Convert basic HTML + text = re.sub(r'', '\n\n', text) + text = re.sub(r'

', '', text) + text = re.sub(r'', '\n', text) + text = re.sub(r'(.*?)', r'**\1**', text) + text = re.sub(r'(.*?)', r'*\1*', text) + text = re.sub(r'(.*?)', r'*\1*', text) + text = re.sub(r'(.*?)', r'**\1**', text) + text = re.sub(r'(.*?)', r'`\1`', text) + text = re.sub(r'

(.*?)

', r'### \1', text) + text = re.sub(r'

(.*?)

', r'#### \1', text) + text = re.sub(r'
    ', '', text) + text = re.sub(r'
', '', text) + text = re.sub(r'
  • (.*?)
  • ', r'- \1', text, flags=re.DOTALL) + text = re.sub(r'
  • ', '- ', text) + + # Remove remaining HTML tags (except
    )
    +    text = re.sub(r'<(?!pre|/pre)[^>]+>', '', text)
    +
    +    return text.strip()
    +
    +
    +# ──────────────────────────────────────────────
    +# Java Source File Parser
    +# ──────────────────────────────────────────────
    +
    +class JavaComponent:
    +    """Represents a parsed Java component (class, interface, enum, annotation)."""
    +
    +    def __init__(self):
    +        self.name = ""
    +        self.package = ""
    +        self.module = ""
    +        self.component_type = ""  # class, interface, enum, annotation
    +        self.description = ""
    +        self.since_version = ""
    +        self.author = ""
    +        self.see_refs = []
    +        self.examples = []
    +        self.extends_class = ""
    +        self.implements_interfaces = []
    +        self.declaration_line = ""
    +        self.public_methods = []
    +        self.source_path = ""
    +
    +    @property
    +    def fully_qualified_name(self):
    +        if self.package:
    +            return f"{self.package}.{self.name}"
    +        return self.name
    +
    +    @property
    +    def wiki_page_name(self):
    +        """Generate a wiki-friendly page name."""
    +        return self.fully_qualified_name.replace('.', '-')
    +
    +
    +class JavaMethod:
    +    """Represents a parsed public method."""
    +
    +    def __init__(self):
    +        self.name = ""
    +        self.signature = ""
    +        self.description = ""
    +        self.since_version = ""
    +        self.examples = []
    +        self.params = []
    +
    +
    +def parse_java_file(filepath, module_name):
    +    """Parse a Java source file and extract component information."""
    +    try:
    +        with open(filepath, 'r', encoding='utf-8') as f:
    +            content = f.read()
    +    except (IOError, UnicodeDecodeError):
    +        return None
    +
    +    # Extract package
    +    pkg_match = PACKAGE_RE.search(content)
    +    package_name = pkg_match.group(1) if pkg_match else ""
    +
    +    # Check for package-info.java
    +    if os.path.basename(filepath) == 'package-info.java':
    +        return None
    +
    +    # Find the class/interface/enum/annotation declaration
    +    # First, find the Javadoc that precedes the class declaration
    +    class_javadoc = None
    +    class_decl_match = None
    +
    +    # Strategy: find all Javadoc blocks and the class declaration
    +    javadoc_blocks = list(JAVADOC_BLOCK_RE.finditer(content))
    +
    +    # Find the main type declaration
    +    for line in content.split('\n'):
    +        stripped = line.strip()
    +        if re.match(r'(?:public\s+)?(?:abstract\s+)?(?:final\s+)?(?:class|interface|enum|@interface)\s+', stripped):
    +            class_decl_match = stripped
    +            break
    +
    +    if not class_decl_match:
    +        return None
    +
    +    # Determine component type (check @interface before interface)
    +    comp_type = "class"
    +    if re.search(r'@interface\s+', class_decl_match):
    +        comp_type = "annotation"
    +    elif re.search(r'\binterface\s+', class_decl_match):
    +        comp_type = "interface"
    +    elif re.search(r'\benum\s+', class_decl_match):
    +        comp_type = "enum"
    +
    +    # Extract class name
    +    name_match = re.search(
    +        r'(?:class|interface|enum|@interface)\s+(\w+)', class_decl_match
    +    )
    +    if not name_match:
    +        return None
    +
    +    class_name = name_match.group(1)
    +
    +    # Skip non-public classes, inner classes, and module-info
    +    if 'public' not in class_decl_match and comp_type != "annotation":
    +        # Check if the file name matches the class name (top-level class)
    +        file_basename = os.path.splitext(os.path.basename(filepath))[0]
    +        if file_basename != class_name:
    +            return None
    +
    +    if class_name in ('module-info', 'package-info'):
    +        return None
    +
    +    # Find the class-level Javadoc (the last Javadoc before the class declaration)
    +    class_decl_pos = content.find(class_decl_match)
    +    for jd_block in reversed(javadoc_blocks):
    +        if jd_block.end() <= class_decl_pos:
    +            # Verify there's no other declaration between this Javadoc and the class
    +            between = content[jd_block.end():class_decl_pos].strip()
    +            # Remove annotations between Javadoc and class decl
    +            between_cleaned = re.sub(r'@\w+(?:\([^)]*\))?', '', between).strip()
    +            if not between_cleaned or between_cleaned.startswith('@'):
    +                class_javadoc = jd_block.group(1)
    +                break
    +
    +    # Parse the class Javadoc
    +    javadoc_info = parse_javadoc(class_javadoc)
    +
    +    # Check for @Since annotation on the class (limit search to nearby context)
    +    search_start = max(0, class_decl_pos - 2000)
    +    since_annotation = SINCE_ANNOTATION_RE.search(content[search_start:class_decl_pos + len(class_decl_match)])
    +    annotation_since = since_annotation.group(1) if since_annotation else ""
    +
    +    # Extract extends/implements
    +    extends_match = re.search(r'\bextends\s+([\w.<>, ]+?)(?:\s+implements|\s*\{)', class_decl_match)
    +    extends_class = extends_match.group(1).strip() if extends_match else ""
    +
    +    implements_match = re.search(r'\bimplements\s+([\w.<>, ]+?)(?:\s*\{|$)', class_decl_match)
    +    implements_interfaces = []
    +    if implements_match:
    +        impl_str = implements_match.group(1).strip().rstrip('{').strip()
    +        implements_interfaces = [i.strip() for i in impl_str.split(',') if i.strip()]
    +
    +    # Parse public methods
    +    public_methods = extract_public_methods(content, class_decl_pos)
    +
    +    # Build the component
    +    component = JavaComponent()
    +    component.name = class_name
    +    component.package = package_name
    +    component.module = module_name
    +    component.component_type = comp_type
    +    component.description = javadoc_info["description"]
    +    component.since_version = javadoc_info["since"] or annotation_since
    +    component.author = javadoc_info["author"]
    +    component.see_refs = javadoc_info["see"]
    +    component.examples = javadoc_info["examples"]
    +    component.extends_class = extends_class
    +    component.implements_interfaces = implements_interfaces
    +    component.declaration_line = class_decl_match.rstrip('{').strip()
    +    component.public_methods = public_methods
    +    component.source_path = os.path.relpath(filepath, PROJECT_ROOT)
    +
    +    return component
    +
    +
    +def extract_public_methods(content, class_start_pos):
    +    """Extract public methods from the class body."""
    +    methods = []
    +    # Only look at content after class declaration
    +    body = content[class_start_pos:]
    +
    +    for match in PUBLIC_METHOD_RE.finditer(body):
    +        javadoc_text = match.group(1)
    +        full_signature = match.group(2)
    +        method_name = match.group(3)
    +
    +        # Skip constructors, getters/setters that are trivial
    +        if method_name in ('toString', 'hashCode', 'equals', 'clone'):
    +            continue
    +
    +        method = JavaMethod()
    +        method.name = method_name
    +        method.signature = full_signature.strip()
    +
    +        if javadoc_text:
    +            method_jd = parse_javadoc(javadoc_text)
    +            method.description = method_jd["description"]
    +            method.since_version = method_jd["since"]
    +            method.examples = method_jd["examples"]
    +
    +        methods.append(method)
    +
    +    return methods[:20]  # Limit to 20 methods per class to keep docs manageable
    +
    +
    +# ──────────────────────────────────────────────
    +# Wiki Page Generator
    +# ──────────────────────────────────────────────
    +
    +def generate_wiki_page(component):
    +    """Generate a Markdown wiki page for a Java component."""
    +    lines = []
    +
    +    # Title
    +    type_label = component.component_type.capitalize()
    +    lines.append(f"# {component.name}")
    +    lines.append("")
    +
    +    # Metadata badge line
    +    badges = []
    +    badges.append(f"**Type:** `{type_label}`")
    +    badges.append(f"**Module:** `{component.module}`")
    +    badges.append(f"**Package:** `{component.package}`")
    +    if component.since_version:
    +        badges.append(f"**Since:** `{component.since_version}`")
    +    lines.append(" | ".join(badges))
    +    lines.append("")
    +
    +    # Source link
    +    lines.append(f"> **Source:** [`{component.source_path}`]"
    +                 f"(https://github.com/microsphere-projects/{ARTIFACT_ID}/blob/main/{component.source_path})")
    +    lines.append("")
    +
    +    # ── Overview ──
    +    lines.append("## Overview")
    +    lines.append("")
    +    if component.description:
    +        desc_md = convert_javadoc_to_markdown(component.description)
    +        lines.append(desc_md)
    +    else:
    +        lines.append(f"`{component.name}` is a {type_label.lower()} in the "
    +                     f"`{component.package}` package of the `{component.module}` module.")
    +    lines.append("")
    +
    +    # Declaration
    +    lines.append("### Declaration")
    +    lines.append("")
    +    lines.append("```java")
    +    lines.append(component.declaration_line)
    +    lines.append("```")
    +    lines.append("")
    +
    +    # ── Author ──
    +    if component.author:
    +        lines.append(f"**Author:** {component.author}")
    +        lines.append("")
    +
    +    # ── Since / Version Info ──
    +    lines.append("## Version Information")
    +    lines.append("")
    +    if component.since_version:
    +        lines.append(f"- **Introduced in:** `{component.since_version}`")
    +    else:
    +        lines.append(f"- **Introduced in:** `{PROJECT_VERSION}` (current)")
    +    lines.append(f"- **Current Project Version:** `{PROJECT_VERSION}`")
    +    lines.append("")
    +
    +    # ── Version Compatibility ──
    +    lines.append("## Version Compatibility")
    +    lines.append("")
    +    lines.append("This component is tested and compatible with the following Java versions:")
    +    lines.append("")
    +    lines.append("| Java Version | Status |")
    +    lines.append("|:---:|:---:|")
    +    for v in JAVA_VERSIONS:
    +        lines.append(f"| Java {v} | ✅ Compatible |")
    +    lines.append("")
    +
    +    # ── Examples ──
    +    has_examples = bool(component.examples)
    +    if not has_examples:
    +        # Check methods for examples
    +        for method in component.public_methods:
    +            if method.examples:
    +                has_examples = True
    +                break
    +
    +    if has_examples:
    +        lines.append("## Examples")
    +        lines.append("")
    +
    +        if component.examples:
    +            for i, example in enumerate(component.examples, 1):
    +                if len(component.examples) > 1:
    +                    lines.append(f"### Example {i}")
    +                    lines.append("")
    +                lines.append("```java")
    +                lines.append(example)
    +                lines.append("```")
    +                lines.append("")
    +
    +        # Method-level examples
    +        method_examples_added = False
    +        for method in component.public_methods:
    +            if method.examples:
    +                if not method_examples_added:
    +                    lines.append(f"### Method Examples")
    +                    lines.append("")
    +                    method_examples_added = True
    +                lines.append(f"#### `{method.name}`")
    +                lines.append("")
    +                for example in method.examples:
    +                    lines.append("```java")
    +                    lines.append(example)
    +                    lines.append("```")
    +                    lines.append("")
    +
    +    # ── Usage Guide ──
    +    lines.append("## Usage")
    +    lines.append("")
    +    lines.append("### Maven Dependency")
    +    lines.append("")
    +    lines.append("Add the following dependency to your `pom.xml`:")
    +    lines.append("")
    +    lines.append("```xml")
    +    lines.append("")
    +    lines.append("    io.github.microsphere-projects")
    +    lines.append(f"    {component.module}")
    +    lines.append(f"    ${{{ARTIFACT_ID}.version}}")
    +    lines.append("")
    +    lines.append("```")
    +    lines.append("")
    +    lines.append(f"> **Tip:** Use the BOM (`{ARTIFACT_ID}-dependencies`) for consistent version management. "
    +                 f"See the [Getting Started](https://github.com/microsphere-projects/{ARTIFACT_ID}#getting-started) guide.")
    +    lines.append("")
    +
    +    # ── Import ──
    +    lines.append("### Import")
    +    lines.append("")
    +    lines.append("```java")
    +    lines.append(f"import {component.fully_qualified_name};")
    +    lines.append("```")
    +    lines.append("")
    +
    +    # ── Public API ──
    +    if component.public_methods:
    +        lines.append("## API Reference")
    +        lines.append("")
    +        lines.append("### Public Methods")
    +        lines.append("")
    +        lines.append("| Method | Description |")
    +        lines.append("|--------|-------------|")
    +        for method in component.public_methods:
    +            desc = method.description.split('\n')[0] if method.description else ""
    +            desc = convert_javadoc_to_markdown(desc)
    +            # Truncate long descriptions for the table
    +            if len(desc) > 120:
    +                desc = desc[:117] + "..."
    +            sig = method.signature.replace('|', '\\|')
    +            lines.append(f"| `{method.name}` | {desc} |")
    +        lines.append("")
    +
    +        # Detailed method descriptions
    +        has_detailed_methods = any(
    +            m.description and len(m.description) > 50 for m in component.public_methods
    +        )
    +        if has_detailed_methods:
    +            lines.append("### Method Details")
    +            lines.append("")
    +            for method in component.public_methods:
    +                if method.description and len(method.description) > 50:
    +                    lines.append(f"#### `{method.name}`")
    +                    lines.append("")
    +                    lines.append(f"```java")
    +                    lines.append(method.signature)
    +                    lines.append("```")
    +                    lines.append("")
    +                    desc_md = convert_javadoc_to_markdown(method.description)
    +                    lines.append(desc_md)
    +                    lines.append("")
    +                    if method.since_version:
    +                        lines.append(f"*Since: {method.since_version}*")
    +                        lines.append("")
    +
    +    # ── See Also ──
    +    if component.see_refs:
    +        lines.append("## See Also")
    +        lines.append("")
    +        for ref in component.see_refs:
    +            ref_clean = ref.strip()
    +            if ref_clean:
    +                lines.append(f"- `{ref_clean}`")
    +        lines.append("")
    +
    +    # ── Footer ──
    +    lines.append("---")
    +    lines.append("")
    +    lines.append(f"*This documentation was auto-generated from the source code of "
    +                 f"[{ARTIFACT_ID}](https://github.com/microsphere-projects/{ARTIFACT_ID}).*")
    +    lines.append("")
    +
    +    return '\n'.join(lines)
    +
    +
    +def generate_home_page(components_by_module):
    +    """Generate the Home (index) wiki page."""
    +    lines = []
    +    lines.append(f"# {PROJECT_TITLE} - API Documentation")
    +    lines.append("")
    +    lines.append(f"Welcome to the **{PROJECT_TITLE}** wiki! This documentation is auto-generated "
    +                 f"from the project source code and provides detailed information about each Java component.")
    +    lines.append("")
    +    lines.append("## Project Information")
    +    lines.append("")
    +    lines.append(f"- **Current Version:** `{PROJECT_VERSION}`")
    +    lines.append(f"- **Java Compatibility:** {', '.join('Java ' + v for v in JAVA_VERSIONS)}")
    +    lines.append("- **License:** Apache License 2.0")
    +    lines.append(f"- **Repository:** [microsphere-projects/{ARTIFACT_ID}]"
    +                 f"(https://github.com/microsphere-projects/{ARTIFACT_ID})")
    +    lines.append("")
    +
    +    # Table of Contents by module
    +    lines.append("## Modules")
    +    lines.append("")
    +
    +    for module_name, components in components_by_module.items():
    +        lines.append(f"### {module_name}")
    +        lines.append("")
    +
    +        # Group by package
    +        by_package = OrderedDict()
    +        for comp in components:
    +            pkg = comp.package or "(default)"
    +            if pkg not in by_package:
    +                by_package[pkg] = []
    +            by_package[pkg].append(comp)
    +
    +        for pkg, comps in by_package.items():
    +            lines.append(f"**`{pkg}`**")
    +            lines.append("")
    +            for comp in sorted(comps, key=lambda c: c.name):
    +                type_icon = {
    +                    "class": "📦",
    +                    "interface": "🔌",
    +                    "enum": "🔢",
    +                    "annotation": "🏷️",
    +                }.get(comp.component_type, "📄")
    +                wiki_link = comp.wiki_page_name
    +                lines.append(f"- {type_icon} [{comp.name}]({wiki_link}) - "
    +                             f"{comp.component_type.capitalize()}"
    +                             f"{' - Since ' + comp.since_version if comp.since_version else ''}")
    +            lines.append("")
    +
    +    # Quick links
    +    lines.append("## Quick Links")
    +    lines.append("")
    +    lines.append(f"- [Getting Started](https://github.com/microsphere-projects/{ARTIFACT_ID}#getting-started)")
    +    lines.append(f"- [Building from Source](https://github.com/microsphere-projects/{ARTIFACT_ID}#building-from-source)")
    +    lines.append(f"- [Contributing](https://github.com/microsphere-projects/{ARTIFACT_ID}#contributing)")
    +    lines.append("- [JavaDoc](https://javadoc.io/doc/io.github.microsphere-projects)")
    +    lines.append("")
    +    lines.append("---")
    +    lines.append("")
    +    lines.append(f"*This wiki is auto-generated from the source code of "
    +                 f"[{ARTIFACT_ID}](https://github.com/microsphere-projects/{ARTIFACT_ID}). "
    +                 f"To update, trigger the `wiki-publish` workflow.*")
    +    lines.append("")
    +
    +    return '\n'.join(lines)
    +
    +
    +def generate_sidebar(components_by_module):
    +    """Generate the _Sidebar wiki page for navigation."""
    +    lines = []
    +    lines.append("**[Home](Home)**")
    +    lines.append("")
    +
    +    for module_name, components in components_by_module.items():
    +        # Shorten module name for sidebar
    +        short_name = module_name.replace("microsphere-", "")
    +        lines.append(f"**{short_name}**")
    +        lines.append("")
    +        for comp in sorted(components, key=lambda c: c.name):
    +            wiki_link = comp.wiki_page_name
    +            lines.append(f"- [{comp.name}]({wiki_link})")
    +        lines.append("")
    +
    +    return '\n'.join(lines)
    +
    +
    +# ──────────────────────────────────────────────
    +# Main
    +# ──────────────────────────────────────────────
    +
    +def discover_java_files(project_root, modules):
    +    """Discover all main Java source files in the given modules."""
    +    java_files = []
    +    for module in modules:
    +        src_dir = os.path.join(project_root, module, SRC_MAIN_JAVA)
    +        if not os.path.isdir(src_dir):
    +            continue
    +        for root, _dirs, files in os.walk(src_dir):
    +            for fname in files:
    +                if fname.endswith('.java') and fname != 'package-info.java' and fname != 'module-info.java':
    +                    java_files.append((os.path.join(root, fname), module))
    +    return java_files
    +
    +
    +def main():
    +    parser = argparse.ArgumentParser(description=f"Generate wiki documentation for {ARTIFACT_ID}")
    +    parser.add_argument(
    +        "--output", "-o",
    +        default=os.path.join(PROJECT_ROOT, "wiki"),
    +        help="Output directory for generated wiki pages (default: /wiki)",
    +    )
    +    parser.add_argument(
    +        "--project-root",
    +        default=PROJECT_ROOT,
    +        help=f"Root directory of the {ARTIFACT_ID} project",
    +    )
    +    args = parser.parse_args()
    +
    +    project_root = args.project_root
    +    output_dir = args.output
    +
    +    print(f"{PROJECT_TITLE} Wiki Documentation Generator")
    +    print(f"  Project root: {project_root}")
    +    print(f"  Output dir:   {output_dir}")
    +    print()
    +
    +    # Discover Java files
    +    java_files = discover_java_files(project_root, MODULES)
    +    print(f"Found {len(java_files)} Java source files across {len(MODULES)} modules")
    +    print()
    +
    +    # Parse all files
    +    components = []
    +    for filepath, module_name in java_files:
    +        component = parse_java_file(filepath, module_name)
    +        if component:
    +            components.append(component)
    +
    +    print(f"Parsed {len(components)} Java components")
    +    print()
    +
    +    # Group by module
    +    components_by_module = OrderedDict()
    +    for module in MODULES:
    +        module_components = [c for c in components if c.module == module]
    +        if module_components:
    +            components_by_module[module] = sorted(module_components, key=lambda c: (c.package, c.name))
    +
    +    # Create output directory
    +    os.makedirs(output_dir, exist_ok=True)
    +
    +    # Generate individual wiki pages
    +    page_count = 0
    +    for module_name, module_components in components_by_module.items():
    +        for comp in module_components:
    +            page_content = generate_wiki_page(comp)
    +            page_filename = f"{comp.wiki_page_name}.md"
    +            page_path = os.path.join(output_dir, page_filename)
    +            with open(page_path, 'w', encoding='utf-8') as f:
    +                f.write(page_content)
    +            page_count += 1
    +
    +    print(f"Generated {page_count} wiki pages")
    +
    +    # Generate Home page
    +    home_content = generate_home_page(components_by_module)
    +    with open(os.path.join(output_dir, "Home.md"), 'w', encoding='utf-8') as f:
    +        f.write(home_content)
    +    print("Generated Home.md")
    +
    +    # Generate Sidebar
    +    sidebar_content = generate_sidebar(components_by_module)
    +    with open(os.path.join(output_dir, "_Sidebar.md"), 'w', encoding='utf-8') as f:
    +        f.write(sidebar_content)
    +    print("Generated _Sidebar.md")
    +
    +    print()
    +    print(f"Wiki documentation generated successfully in: {output_dir}")
    +    print(f"Total pages: {page_count + 2} ({page_count} components + Home + Sidebar)")
    +
    +    return 0
    +
    +
    +if __name__ == "__main__":
    +    sys.exit(main())
    diff --git a/.github/workflows/wiki-publish.yml b/.github/workflows/wiki-publish.yml
    new file mode 100644
    index 00000000..53f78fb6
    --- /dev/null
    +++ b/.github/workflows/wiki-publish.yml
    @@ -0,0 +1,79 @@
    +name: Generate and Publish Wiki Documentation
    +
    +on:
    +  push:
    +    branches: [ main ]
    +    paths:
    +      - '*/src/main/java/**/*.java'
    +      - '.github/scripts/generate-wiki-docs.py'
    +      - '.github/workflows/wiki-publish.yml'
    +  workflow_dispatch:
    +
    +permissions:
    +  contents: write
    +
    +jobs:
    +  generate-wiki:
    +    name: Generate Wiki Documentation
    +    runs-on: ubuntu-latest
    +
    +    steps:
    +      - name: Checkout source repository
    +        uses: actions/checkout@v5
    +        with:
    +          fetch-depth: 1
    +
    +      - name: Set up Python
    +        uses: actions/setup-python@v5
    +        with:
    +          python-version: '3.x'
    +
    +      - name: Generate wiki pages
    +        run: |
    +          python .github/scripts/generate-wiki-docs.py --output wiki-output
    +          echo "Generated wiki pages:"
    +          ls -la wiki-output/ | head -20
    +          echo "Total pages: $(ls wiki-output/*.md | wc -l)"
    +
    +      - name: Checkout wiki repository
    +        uses: actions/checkout@v5
    +        with:
    +          repository: ${{ github.repository }}.wiki
    +          path: wiki-repo
    +          token: ${{ secrets.GITHUB_TOKEN }}
    +        continue-on-error: true
    +
    +      - name: Initialize wiki repository if needed
    +        run: |
    +          if [ ! -d "wiki-repo/.git" ]; then
    +            echo "Wiki repository not found. Creating initial wiki structure..."
    +            mkdir -p wiki-repo
    +            cd wiki-repo
    +            git init
    +            git remote add origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.wiki.git"
    +          fi
    +
    +      - name: Copy generated pages to wiki
    +        run: |
    +          # Remove all existing markdown files from wiki repo to ensure
    +          # pages for renamed or deleted classes are properly cleaned up
    +          find wiki-repo -maxdepth 1 -name '*.md' -type f -delete 2>/dev/null || true
    +          # Copy all generated markdown files to the wiki repo
    +          cp wiki-output/*.md wiki-repo/
    +          echo "Copied wiki pages to wiki repository"
    +          ls -la wiki-repo/*.md | head -20
    +
    +      - name: Push to wiki
    +        run: |
    +          cd wiki-repo
    +          git config user.name "github-actions[bot]"
    +          git config user.email "github-actions[bot]@users.noreply.github.com"
    +          git add -A
    +          if git diff --cached --quiet; then
    +            echo "No changes to wiki documentation"
    +          else
    +            TIMESTAMP=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
    +            git commit -m "Update wiki documentation - ${TIMESTAMP}"
    +            git push origin HEAD:master || git push origin HEAD:main
    +            echo "Wiki documentation updated successfully"
    +          fi
    
    From 3733a254e7f1ee6cda60fc052ee4278210dec5f3 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Thu, 26 Mar 2026 21:30:33 +0800
    Subject: [PATCH 299/310] Enable Spring test logging and tweak Feign config
    
    Annotate multiple test classes with @SpringLoggingTest to enable consistent test logging and add a new FeignClientSpecificationPostProcessorTest. Adjust Feign auto-refresh code: simplify ConditionalOnBean to use Marker, statically import NoOpRequestInterceptor.INSTANCE, and make AUTO_REFRESH_CAPABILITY_CLASS, FEIGN_CLIENT_SPECIFICATION_CLASS and injectAutoRefreshCapability package-private to allow testing. Update parent POM to add junit-jupiter.version and import the JUnit BOM for consistent test dependency management.
    ---
     ...bServiceRegistryAutoConfigurationTest.java |  2 +
     .../BaseServiceRegistrationEndpointTest.java  |  2 +
     .../util/ServiceInstanceUtilsTest.java        |  2 +
     ...omcatDynamicConfigurationListenerTest.java |  2 +
     ...ignClientAutoRefreshAutoConfiguration.java |  7 ++-
     ...FeignClientSpecificationPostProcessor.java |  6 +-
     ...nClientSpecificationPostProcessorTest.java | 56 +++++++++++++++++++
     .../DecoratedFeignComponentTest.java          |  2 +
     microsphere-spring-cloud-parent/pom.xml       | 10 ++++
     9 files changed, 84 insertions(+), 5 deletions(-)
     create mode 100644 microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessorTest.java
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java
    index e631368e..c67bbb4b 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java
    @@ -18,6 +18,7 @@
     package io.microsphere.spring.cloud.client.service.registry.autoconfigure;
     
     
    +import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
     import io.microsphere.spring.test.web.controller.TestController;
     import io.microsphere.spring.web.metadata.WebEndpointMapping;
     import org.junit.jupiter.api.Test;
    @@ -51,6 +52,7 @@
             },
             webEnvironment = RANDOM_PORT
     )
    +@SpringLoggingTest
     abstract class WebServiceRegistryAutoConfigurationTest {
     
         @Autowired
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java
    index cbd55d63..d71d4581 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java
    @@ -18,6 +18,7 @@
     package io.microsphere.spring.cloud.client.service.registry.endpoint;
     
     
    +import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
     import org.junit.jupiter.api.extension.ExtendWith;
     import org.springframework.beans.factory.annotation.Autowired;
     import org.springframework.boot.test.context.SpringBootTest;
    @@ -48,6 +49,7 @@
             },
             webEnvironment = RANDOM_PORT
     )
    +@SpringLoggingTest
     class BaseServiceRegistrationEndpointTest {
     
         @Autowired
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java
    index ef194316..0e1d9cf0 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java
    @@ -19,6 +19,7 @@
     
     
     import io.microsphere.json.JSONObject;
    +import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
     import io.microsphere.spring.web.metadata.WebEndpointMapping;
     import io.microsphere.spring.web.metadata.WebEndpointMapping.Builder;
     import org.junit.jupiter.api.BeforeEach;
    @@ -61,6 +62,7 @@
      * @see ServiceInstanceUtils
      * @since 1.0.0
      */
    +@SpringLoggingTest
     public class ServiceInstanceUtilsTest {
     
         private static final Integer WEB_ENDPOINT_MAPPING_ID = Integer.valueOf(12345);
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java
    index 58d3f3be..f2e67d85 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListenerTest.java
    @@ -18,6 +18,7 @@
     package io.microsphere.spring.cloud.fault.tolerance.tomcat.event;
     
     
    +import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
     import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.api.Test;
     import org.springframework.boot.autoconfigure.web.ServerProperties;
    @@ -37,6 +38,7 @@
      * @see TomcatDynamicConfigurationListener
      * @since 1.0.0
      */
    +@SpringLoggingTest
     class TomcatDynamicConfigurationListenerTest {
     
         private ServerProperties serverProperties;
    diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java
    index 7e851e56..8d1fe2c5 100644
    --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java
    +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java
    @@ -1,5 +1,6 @@
     package io.microsphere.spring.cloud.openfeign.autoconfigure;
     
    +import io.microsphere.spring.cloud.openfeign.autoconfigure.EnableFeignAutoRefresh.Marker;
     import io.microsphere.spring.cloud.openfeign.autorefresh.FeignClientConfigurationChangedListener;
     import io.microsphere.spring.cloud.openfeign.autorefresh.FeignComponentRegistry;
     import io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor;
    @@ -12,6 +13,8 @@
     import org.springframework.context.annotation.Bean;
     import org.springframework.context.event.EventListener;
     
    +import static io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor.INSTANCE;
    +
     /**
      * The Auto-Configuration class for {@link EnableFeignAutoRefresh}
      *
    @@ -20,7 +23,7 @@
      * @see EnableFeignAutoRefresh
      * @since 0.0.1
      */
    -@ConditionalOnBean(EnableFeignAutoRefresh.Marker.class)
    +@ConditionalOnBean(Marker.class)
     public class FeignClientAutoRefreshAutoConfiguration {
     
         /**
    @@ -38,7 +41,7 @@ public class FeignClientAutoRefreshAutoConfiguration {
         @Bean
         public FeignBuilderCustomizer addDefaultRequestInterceptorCustomizer() {
             return builder -> {
    -            builder.requestInterceptor(NoOpRequestInterceptor.INSTANCE);
    +            builder.requestInterceptor(INSTANCE);
             };
         }
     
    diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java
    index 4eb359bf..1ae0ec5e 100644
    --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java
    +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java
    @@ -23,9 +23,9 @@ public class FeignClientSpecificationPostProcessor implements BeanPostProcessor
     
         private static final Logger logger = getLogger(FeignClientSpecificationPostProcessor.class);
     
    -    private static final Class AUTO_REFRESH_CAPABILITY_CLASS = AutoRefreshCapability.class;
    +    static final Class AUTO_REFRESH_CAPABILITY_CLASS = AutoRefreshCapability.class;
     
    -    private static final Class FEIGN_CLIENT_SPECIFICATION_CLASS = FeignClientSpecification.class;
    +    static final Class FEIGN_CLIENT_SPECIFICATION_CLASS = FeignClientSpecification.class;
     
         /**
          * Injects the {@link AutoRefreshCapability} into default {@link FeignClientSpecification}
    @@ -51,7 +51,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
             return bean;
         }
     
    -    private void injectAutoRefreshCapability(FeignClientSpecification specification) {
    +    void injectAutoRefreshCapability(FeignClientSpecification specification) {
             Class[] originConfigurationClasses = specification.getConfiguration();
             Class[] newConfigurationClasses = combine(AUTO_REFRESH_CAPABILITY_CLASS, originConfigurationClasses);
             specification.setConfiguration(newConfigurationClasses);
    diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessorTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessorTest.java
    new file mode 100644
    index 00000000..fc67907b
    --- /dev/null
    +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessorTest.java
    @@ -0,0 +1,56 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +
    +package io.microsphere.spring.cloud.openfeign.autoconfigure;
    +
    +
    +import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Test;
    +import org.springframework.cloud.openfeign.FeignClientSpecification;
    +
    +import static io.microsphere.spring.cloud.openfeign.autoconfigure.FeignClientSpecificationPostProcessor.AUTO_REFRESH_CAPABILITY_CLASS;
    +import static io.microsphere.util.ArrayUtils.combine;
    +import static io.microsphere.util.ArrayUtils.ofArray;
    +import static org.junit.jupiter.api.Assertions.assertArrayEquals;
    +
    +/**
    + * {@link FeignClientSpecificationPostProcessor} Test
    + *
    + * @author Mercy
    + * @see FeignClientSpecificationPostProcessor
    + * @since 1.0.0
    + */
    +@SpringLoggingTest
    +class FeignClientSpecificationPostProcessorTest {
    +
    +    private FeignClientSpecificationPostProcessor postProcessor;
    +
    +    @BeforeEach
    +    void setUp() {
    +        this.postProcessor = new FeignClientSpecificationPostProcessor();
    +    }
    +
    +    @Test
    +    void testInjectAutoRefreshCapability() {
    +        Class[] configurationClasses = ofArray(FeignClientSpecificationPostProcessorTest.class);
    +        FeignClientSpecification specification = new FeignClientSpecification("test", "TestClass", configurationClasses);
    +        assertArrayEquals(configurationClasses, specification.getConfiguration());
    +        this.postProcessor.injectAutoRefreshCapability(specification);
    +        assertArrayEquals(combine(AUTO_REFRESH_CAPABILITY_CLASS, configurationClasses), specification.getConfiguration());
    +    }
    +}
    \ No newline at end of file
    diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java
    index 1efd73a4..48365e09 100644
    --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java
    +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponentTest.java
    @@ -20,6 +20,7 @@
     import feign.Request;
     import feign.RequestTemplate;
     import feign.Response;
    +import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
     import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.api.Test;
     import org.springframework.cloud.context.named.NamedContextFactory;
    @@ -51,6 +52,7 @@
      * @see DecoratedFeignComponent
      * @since 1.0.0
      */
    +@SpringLoggingTest
     abstract class DecoratedFeignComponentTest> {
     
         protected String contextId;
    diff --git a/microsphere-spring-cloud-parent/pom.xml b/microsphere-spring-cloud-parent/pom.xml
    index 41a4be0e..1727b7ea 100644
    --- a/microsphere-spring-cloud-parent/pom.xml
    +++ b/microsphere-spring-cloud-parent/pom.xml
    @@ -22,10 +22,20 @@
             
             0.2.6
             2.0.4
    +        
    +        6.0.3
         
     
         
             
    +            
    +            
    +                org.junit
    +                junit-bom
    +                ${junit-jupiter.version}
    +                pom
    +                import
    +            
     
                 
                 
    
    From ae35c7a7ddace7698fbd3a9117d13e2bc054d674 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Thu, 26 Mar 2026 21:40:06 +0800
    Subject: [PATCH 300/310] Add index parameter to testStart
    
    Change testStart() to testStart(int index) and wrap its assertions in an if (index == 0) block so the assertions only run for index 0. This adapts the test for parameterized or repeated invocations while preserving the original assertion behavior.
    ---
     .../endpoint/ServiceRegistrationEndpointTest.java         | 8 +++++---
     1 file changed, 5 insertions(+), 3 deletions(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java
    index fba4e41f..cc406753 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java
    @@ -64,8 +64,10 @@ void testMetadata() {
         }
     
         @Test
    -    void testStart() {
    -        assertFalse(this.endpoint.start());
    -        assertTrue(this.endpoint.start());
    +    void testStart(int index) {
    +        if (index == 0) {
    +            assertFalse(this.endpoint.start());
    +            assertTrue(this.endpoint.start());
    +        }
         }
     }
    \ No newline at end of file
    
    From f49aaed45e227a762f774cee800a15d5a73d0a51 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 10:58:30 +0800
    Subject: [PATCH 301/310] Relax excludeMappings visibility and param type
    
    Change excludeMappings from private(Set) to package-private(Collection) so it can be accessed within the package and accept any Collection. Iteration logic remains unchanged.
    ---
     .../autoconfigure/WebServiceRegistryAutoConfiguration.java      | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java
    index fe81ee6e..427f88f1 100644
    --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java
    +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java
    @@ -89,7 +89,7 @@ private void attachWebMappingsMetadata(Registration registration, Collection mappings) {
    +    void excludeMappings(Collection mappings) {
             Iterator iterator = mappings.iterator();
             while (iterator.hasNext()) {
                 WebEndpointMapping mapping = iterator.next();
    
    From 7ee5b4aa531ae9e20028b4fa39308d3474bac91c Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 10:58:38 +0800
    Subject: [PATCH 302/310] Remove trailing newline in test file
    
    Remove the final newline character from microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java. This is a non-functional whitespace/formatting change.
    ---
     .../WebMvcServiceRegistryAutoConfigurationTest.java             | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java
    index 09730c57..cf751340 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfigurationTest.java
    @@ -28,4 +28,4 @@
     @EnableWebMvcExtension
     @EnableAutoConfiguration
     class WebMvcServiceRegistryAutoConfigurationTest extends WebServiceRegistryAutoConfigurationTest {
    -}
    +}
    \ No newline at end of file
    
    From f3fdbc5efb1e3605c8239a4df770de99f1a9eb53 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 10:58:49 +0800
    Subject: [PATCH 303/310] Add excludeMappings test; use LoggingLevelsTest
    
    Replace @SpringLoggingTest with @LoggingLevelsTest and update imports. Add a new test (testExcludeMappings) that constructs a WebEndpointMapping, injects WebServiceRegistryAutoConfiguration, and verifies excludeMappings removes the mapping. Also add static imports for newLinkedList, ofList and GET, and adjust references to use this.registration for clarity.
    ---
     ...bServiceRegistryAutoConfigurationTest.java | 29 ++++++++++++++++---
     1 file changed, 25 insertions(+), 4 deletions(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java
    index c67bbb4b..2dd85f88 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfigurationTest.java
    @@ -18,7 +18,7 @@
     package io.microsphere.spring.cloud.client.service.registry.autoconfigure;
     
     
    -import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
    +import io.microsphere.logging.test.jupiter.LoggingLevelsTest;
     import io.microsphere.spring.test.web.controller.TestController;
     import io.microsphere.spring.web.metadata.WebEndpointMapping;
     import org.junit.jupiter.api.Test;
    @@ -29,6 +29,8 @@
     import java.util.Collection;
     import java.util.Map;
     
    +import static io.microsphere.collection.ListUtils.newLinkedList;
    +import static io.microsphere.collection.Lists.ofList;
     import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_CONTEXT_PATH_METADATA_NAME;
     import static io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants.WEB_MAPPINGS_METADATA_NAME;
     import static io.microsphere.spring.cloud.client.service.util.ServiceInstanceUtils.getWebEndpointMappings;
    @@ -36,6 +38,7 @@
     import static org.junit.jupiter.api.Assertions.assertNotNull;
     import static org.junit.jupiter.api.Assertions.assertTrue;
     import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
    +import static org.springframework.http.HttpMethod.GET;
     
     /**
      * {@link WebServiceRegistryAutoConfiguration} Test
    @@ -52,19 +55,37 @@
             },
             webEnvironment = RANDOM_PORT
     )
    -@SpringLoggingTest
     abstract class WebServiceRegistryAutoConfigurationTest {
     
         @Autowired
         private Registration registration;
     
    +    @Autowired
    +    private WebServiceRegistryAutoConfiguration autoConfiguration;
    +
         @Test
         void test() {
    -        Map metadata = registration.getMetadata();
    +        Map metadata = this.registration.getMetadata();
             assertEquals("", metadata.get(WEB_CONTEXT_PATH_METADATA_NAME));
             assertNotNull(metadata.get(WEB_MAPPINGS_METADATA_NAME));
     
    -        Collection webEndpointMappings = getWebEndpointMappings(registration);
    +        Collection webEndpointMappings = getWebEndpointMappings(this.registration);
             assertTrue(webEndpointMappings.size() >= 6);
         }
    +
    +    @Test
    +    @LoggingLevelsTest(levels = "ERROR")
    +    void testExcludeMappings() {
    +        WebEndpointMapping webEndpointMapping = WebEndpointMapping
    +                .webmvc()
    +                .endpoint(this)
    +                .methods(GET)
    +                .pattern("/actuator/test")
    +                .build();
    +
    +        Collection webEndpointMappings = newLinkedList(ofList(webEndpointMapping));
    +
    +        this.autoConfiguration.excludeMappings(webEndpointMappings);
    +        assertTrue(webEndpointMappings.isEmpty());
    +    }
     }
    \ No newline at end of file
    
    From ecbe9f8798eb0d58391a6b7f891eb0a6b000ac9f Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 10:58:59 +0800
    Subject: [PATCH 304/310] Remove SpringLoggingTest annotation from test
    
    Delete the unused import and the @SpringLoggingTest annotation from BaseServiceRegistrationEndpointTest, cleaning up the test class by removing the SpringLoggingTest extension usage.
    ---
     .../registry/endpoint/BaseServiceRegistrationEndpointTest.java  | 2 --
     1 file changed, 2 deletions(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java
    index d71d4581..cbd55d63 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/BaseServiceRegistrationEndpointTest.java
    @@ -18,7 +18,6 @@
     package io.microsphere.spring.cloud.client.service.registry.endpoint;
     
     
    -import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
     import org.junit.jupiter.api.extension.ExtendWith;
     import org.springframework.beans.factory.annotation.Autowired;
     import org.springframework.boot.test.context.SpringBootTest;
    @@ -49,7 +48,6 @@
             },
             webEnvironment = RANDOM_PORT
     )
    -@SpringLoggingTest
     class BaseServiceRegistrationEndpointTest {
     
         @Autowired
    
    From e4182f2cb1335f173f11babb9172d9d836d8a843 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 10:59:07 +0800
    Subject: [PATCH 305/310] Refactor testStart to use LoggingLevelsTest
    
    Replace the previous conditional/parameterized testStart with a simpler test annotated with @LoggingLevelsTest(levels = "ERROR"). The test now asserts that start() returns the same value as isRunning(), called twice, removing redundant true/false assertions and unused static imports. Added the LoggingLevelsTest import and cleaned up imports accordingly.
    ---
     .../endpoint/ServiceRegistrationEndpointTest.java    | 12 +++++-------
     1 file changed, 5 insertions(+), 7 deletions(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java
    index cc406753..77e9aff3 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpointTest.java
    @@ -18,6 +18,7 @@
     package io.microsphere.spring.cloud.client.service.registry.endpoint;
     
     
    +import io.microsphere.logging.test.jupiter.LoggingLevelsTest;
     import org.junit.jupiter.api.Test;
     import org.springframework.beans.factory.annotation.Autowired;
     import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    @@ -28,10 +29,8 @@
     import static java.lang.Boolean.TRUE;
     import static java.lang.Integer.valueOf;
     import static org.junit.jupiter.api.Assertions.assertEquals;
    -import static org.junit.jupiter.api.Assertions.assertFalse;
     import static org.junit.jupiter.api.Assertions.assertNull;
     import static org.junit.jupiter.api.Assertions.assertSame;
    -import static org.junit.jupiter.api.Assertions.assertTrue;
     
     /**
      * {@link ServiceRegistrationEndpoint} Test
    @@ -64,10 +63,9 @@ void testMetadata() {
         }
     
         @Test
    -    void testStart(int index) {
    -        if (index == 0) {
    -            assertFalse(this.endpoint.start());
    -            assertTrue(this.endpoint.start());
    -        }
    +    @LoggingLevelsTest(levels = "ERROR")
    +    void testStart() {
    +        assertEquals(this.endpoint.isRunning(), this.endpoint.start());
    +        assertEquals(this.endpoint.isRunning(), this.endpoint.start());
         }
     }
    \ No newline at end of file
    
    From 81dcefdafae26af0f5ad9eeb15727394c2e373ac Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 10:59:22 +0800
    Subject: [PATCH 306/310] Use LoggingLevelsTest instead of SpringLoggingTest
    
    Replace class-level @SpringLoggingTest with the more granular @LoggingLevelsTest. Updated import to io.microsphere.logging.test.jupiter.LoggingLevelsTest, removed the @SpringLoggingTest annotation on the class, and added @LoggingLevelsTest(levels = "ERROR") to the testAttachMetadata method to scope logging level for that test.
    ---
     .../cloud/client/service/util/ServiceInstanceUtilsTest.java   | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java
    index 0e1d9cf0..55bc457f 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtilsTest.java
    @@ -19,7 +19,7 @@
     
     
     import io.microsphere.json.JSONObject;
    -import io.microsphere.spring.test.junit.jupiter.SpringLoggingTest;
    +import io.microsphere.logging.test.jupiter.LoggingLevelsTest;
     import io.microsphere.spring.web.metadata.WebEndpointMapping;
     import io.microsphere.spring.web.metadata.WebEndpointMapping.Builder;
     import org.junit.jupiter.api.BeforeEach;
    @@ -62,7 +62,6 @@
      * @see ServiceInstanceUtils
      * @since 1.0.0
      */
    -@SpringLoggingTest
     public class ServiceInstanceUtilsTest {
     
         private static final Integer WEB_ENDPOINT_MAPPING_ID = Integer.valueOf(12345);
    @@ -84,6 +83,7 @@ void setUp() {
         }
     
         @Test
    +    @LoggingLevelsTest(levels = "ERROR")
         void testAttachMetadata() {
             attachMetadata(this.context, this.serviceInstance, this.webEndpointMappings);
             assertEquals(this.context, getMetadata(this.serviceInstance, WEB_CONTEXT_PATH_METADATA_NAME));
    
    From a65a1ff8e0d7e875c50908bd9c8cd221bdbbac40 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 11:01:30 +0800
    Subject: [PATCH 307/310] Use assertEquals and add LoggingLevelsTest
    
    Replace assertFalse/assertTrue with assertEquals assertions that compare endpoint.isRunning() to stop()/start() results for clearer intent. Add import and annotate testStop with @LoggingLevelsTest(levels = "ERROR"). Remove unused static imports of assertFalse/assertTrue.
    ---
     .../endpoint/ServiceDeregistrationEndpointTest.java   | 11 ++++++-----
     1 file changed, 6 insertions(+), 5 deletions(-)
    
    diff --git a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java
    index 8320b8cd..c1ea2d7e 100644
    --- a/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java
    +++ b/microsphere-spring-cloud-commons/src/test/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpointTest.java
    @@ -18,13 +18,13 @@
     package io.microsphere.spring.cloud.client.service.registry.endpoint;
     
     
    +import io.microsphere.logging.test.jupiter.LoggingLevelsTest;
     import org.junit.jupiter.api.Test;
     import org.springframework.beans.factory.annotation.Autowired;
     import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
     import org.springframework.test.context.ContextConfiguration;
     
    -import static org.junit.jupiter.api.Assertions.assertFalse;
    -import static org.junit.jupiter.api.Assertions.assertTrue;
    +import static org.junit.jupiter.api.Assertions.assertEquals;
     
     /**
      * {@link ServiceDeregistrationEndpoint} Test
    @@ -47,9 +47,10 @@ class ServiceDeregistrationEndpointTest extends BaseServiceRegistrationEndpointT
         private ServiceDeregistrationEndpoint serviceDeregistrationEndpoint;
     
         @Test
    +    @LoggingLevelsTest(levels = "ERROR")
         void testStop() {
    -        assertFalse(this.serviceDeregistrationEndpoint.stop());
    -        assertFalse(this.serviceRegistrationEndpoint.start());
    -        assertTrue(this.serviceDeregistrationEndpoint.stop());
    +        assertEquals(this.serviceDeregistrationEndpoint.isRunning(), this.serviceDeregistrationEndpoint.stop());
    +        assertEquals(this.serviceRegistrationEndpoint.isRunning(), this.serviceRegistrationEndpoint.start());
    +        assertEquals(this.serviceDeregistrationEndpoint.isRunning(), this.serviceDeregistrationEndpoint.stop());
         }
     }
    \ No newline at end of file
    
    From 0d3a83ee43e660cf60593122b341f7a81f218980 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 11:02:12 +0800
    Subject: [PATCH 308/310] Reorder and consolidate imports in tests
    
    Clean up import ordering and duplicates in test classes. Move the static getLogger import to a consistent location, add or relocate the Logger import as needed, and tidy blank lines in BaseTest.java and ObservableFeignInvocationHandler.java. No functional changes.
    ---
     .../java/io/microsphere/spring/cloud/openfeign/BaseTest.java | 5 +++--
     .../cloud/openfeign/ObservableFeignInvocationHandler.java    | 2 +-
     2 files changed, 4 insertions(+), 3 deletions(-)
    
    diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java
    index d9f98866..6ad69f3c 100644
    --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java
    +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/BaseTest.java
    @@ -1,9 +1,8 @@
     package io.microsphere.spring.cloud.openfeign;
     
    +import io.microsphere.logging.Logger;
     import io.microsphere.spring.cloud.openfeign.autoconfigure.EnableFeignAutoRefresh;
     import org.junit.jupiter.api.Test;
    -import io.microsphere.logging.Logger;
    -import static io.microsphere.logging.LoggerFactory.getLogger;
     import org.springframework.beans.factory.annotation.Autowired;
     import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
     import org.springframework.cloud.endpoint.event.RefreshEvent;
    @@ -21,6 +20,8 @@
     import java.util.Map;
     import java.util.Set;
     
    +import static io.microsphere.logging.LoggerFactory.getLogger;
    +
     /**
      * @author 韩超
      * @since 0.0.1
    diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java
    index d9945196..30a9e51e 100644
    --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java
    +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/ObservableFeignInvocationHandler.java
    @@ -4,7 +4,6 @@
     import feign.ResponseHandler;
     import feign.Target;
     import io.microsphere.logging.Logger;
    -import static io.microsphere.logging.LoggerFactory.getLogger;
     import org.springframework.util.Assert;
     import org.springframework.util.ClassUtils;
     
    @@ -15,6 +14,7 @@
     import java.util.Map;
     
     import static feign.Util.checkNotNull;
    +import static io.microsphere.logging.LoggerFactory.getLogger;
     
     /**
      * @author 韩超
    
    From ca479b991635b15ded3bf88f838d2ece84c7ce76 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 11:02:27 +0800
    Subject: [PATCH 309/310] Reorder static import in AQueryMapEncoder
    
    Move the static import of LoggerFactory.getLogger to be grouped with other imports and add a blank line for improved import formatting in the test AQueryMapEncoder. No functional changes; only import ordering/formatting.
    ---
     .../cloud/openfeign/querymapencoder/AQueryMapEncoder.java      | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java
    index 02e95645..e07793e5 100644
    --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java
    +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/AQueryMapEncoder.java
    @@ -2,11 +2,12 @@
     
     import feign.QueryMapEncoder;
     import io.microsphere.logging.Logger;
    -import static io.microsphere.logging.LoggerFactory.getLogger;
     
     import java.util.HashMap;
     import java.util.Map;
     
    +import static io.microsphere.logging.LoggerFactory.getLogger;
    +
     /**
      * @author 韩超
      * @since 1.0
    
    From 7df91fbc9a6b35d62be4d9d306e88656ce742471 Mon Sep 17 00:00:00 2001
    From: Mercy Ma 
    Date: Fri, 27 Mar 2026 11:02:34 +0800
    Subject: [PATCH 310/310] Reorder static import in BQueryMapEncoder
    
    Adjust import ordering in BQueryMapEncoder test: move the static import of LoggerFactory.getLogger below other imports and add spacing. No functional changes; only formatting/organizing imports.
    ---
     .../cloud/openfeign/querymapencoder/BQueryMapEncoder.java      | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java
    index 30ab38a9..18762064 100644
    --- a/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java
    +++ b/microsphere-spring-cloud-openfeign/src/test/java/io/microsphere/spring/cloud/openfeign/querymapencoder/BQueryMapEncoder.java
    @@ -2,11 +2,12 @@
     
     import feign.QueryMapEncoder;
     import io.microsphere.logging.Logger;
    -import static io.microsphere.logging.LoggerFactory.getLogger;
     
     import java.util.HashMap;
     import java.util.Map;
     
    +import static io.microsphere.logging.LoggerFactory.getLogger;
    +
     /**
      * @author 韩超
      * @since 1.0