From 1c0bcba5877233a7aac642b40068c02b416b2955 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Mon, 31 Mar 2025 08:48:11 +0000 Subject: [PATCH] Add HTTP Service registrar tests Closes gh-33992 --- .../AbstractHttpServiceRegistrar.java | 49 +++-- .../AnnotationHttpServiceRegistrar.java | 2 +- .../HttpServiceProxyRegistryFactoryBean.java | 30 ++- ...stClientProxyRegistryIntegrationTests.java | 8 +- .../AnnotationHttpServiceRegistrarTests.java | 195 ++++++++++++++++++ .../registry/HttpServiceRegistrarTests.java | 194 +++++++++++++++++ .../registry}/echo/EchoA.java | 2 +- .../registry}/echo/EchoB.java | 2 +- .../registry}/greeting/GreetingA.java | 2 +- .../registry}/greeting/GreetingB.java | 2 +- 10 files changed, 435 insertions(+), 51 deletions(-) create mode 100644 spring-web/src/test/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrarTests.java create mode 100644 spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceRegistrarTests.java rename spring-web/src/test/java/org/springframework/web/{client/support => service/registry}/echo/EchoA.java (93%) rename spring-web/src/test/java/org/springframework/web/{client/support => service/registry}/echo/EchoB.java (93%) rename spring-web/src/test/java/org/springframework/web/{client/support => service/registry}/greeting/GreetingA.java (93%) rename spring-web/src/test/java/org/springframework/web/{client/support => service/registry}/greeting/GreetingB.java (93%) diff --git a/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java b/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java index e1eedab89b..1bcfca8944 100644 --- a/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java +++ b/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java @@ -46,6 +46,7 @@ import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; import org.springframework.web.service.annotation.HttpExchange; /** @@ -123,12 +124,17 @@ public abstract class AbstractHttpServiceRegistrar implements @Override public final void registerBeanDefinitions( - AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry beanRegistry, - BeanNameGenerator beanNameGenerator) { + AnnotationMetadata metadata, BeanDefinitionRegistry registry, BeanNameGenerator generator) { - registerHttpServices(new DefaultGroupRegistry(), importingClassMetadata); + registerBeanDefinitions(metadata, registry); + } - String proxyRegistryBeanName = HttpServiceProxyRegistry.class.getName(); + @Override + public final void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry beanRegistry) { + + registerHttpServices(new DefaultGroupRegistry(), metadata); + + String proxyRegistryBeanName = StringUtils.uncapitalize(HttpServiceProxyRegistry.class.getSimpleName()); GenericBeanDefinition proxyRegistryBeanDef; if (!beanRegistry.containsBeanDefinition(proxyRegistryBeanName)) { @@ -142,12 +148,12 @@ public abstract class AbstractHttpServiceRegistrar implements proxyRegistryBeanDef = (GenericBeanDefinition) beanRegistry.getBeanDefinition(proxyRegistryBeanName); } - mergeHttpServices(proxyRegistryBeanDef); + mergeGroups(proxyRegistryBeanDef); this.groupMap.forEach((groupName, group) -> group.httpServiceTypeNames().forEach(type -> { GenericBeanDefinition proxyBeanDef = new GenericBeanDefinition(); proxyBeanDef.setBeanClassName(type); - String beanName = (groupName + "." + beanNameGenerator.generateBeanName(proxyBeanDef, beanRegistry)); + String beanName = (groupName + "#" + type); proxyBeanDef.setInstanceSupplier(() -> getProxyInstance(proxyRegistryBeanName, groupName, type)); if (!beanRegistry.containsBeanDefinition(beanName)) { beanRegistry.registerBeanDefinition(beanName, proxyBeanDef); @@ -155,10 +161,6 @@ public abstract class AbstractHttpServiceRegistrar implements })); } - @Override - public final void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { - } - /** * This method is called before any bean definition registrations are made. * Subclasses must implement it to register the HTTP Services for which bean @@ -181,7 +183,7 @@ public abstract class AbstractHttpServiceRegistrar implements } @SuppressWarnings("unchecked") - private void mergeHttpServices(GenericBeanDefinition proxyRegistryBeanDef) { + private void mergeGroups(GenericBeanDefinition proxyRegistryBeanDef) { ConstructorArgumentValues args = proxyRegistryBeanDef.getConstructorArgumentValues(); ConstructorArgumentValues.ValueHolder valueHolder = args.getArgumentValue(0, Map.class); Assert.state(valueHolder != null, "Expected Map constructor argument at index 0"); @@ -233,7 +235,9 @@ public abstract class AbstractHttpServiceRegistrar implements /** * Perform HTTP Service registrations for the given group. */ - GroupSpec forGroup(String name); + default GroupSpec forGroup(String name) { + return forGroup(name, HttpServiceGroup.ClientType.UNSPECIFIED); + } /** * Variant of {@link #forGroup(String)} with a client type. @@ -278,11 +282,6 @@ public abstract class AbstractHttpServiceRegistrar implements */ private class DefaultGroupRegistry implements GroupRegistry { - @Override - public GroupSpec forGroup(String name) { - return forGroup(name, HttpServiceGroup.ClientType.UNSPECIFIED); - } - @Override public GroupSpec forGroup(String name, HttpServiceGroup.ClientType clientType) { return new DefaultGroupSpec(name, clientType); @@ -313,14 +312,14 @@ public abstract class AbstractHttpServiceRegistrar implements @Override public GroupSpec register(Class... serviceTypes) { - getOrCreateGroup(groupName, clientType).addHttpServiceTypes(serviceTypes); + getOrCreateGroup().addHttpServiceTypes(serviceTypes); return this; } @Override public GroupSpec detectInBasePackages(Class... packageClasses) { for (Class packageClass : packageClasses) { - detect(this.groupName, this.clientType, packageClass.getPackageName()); + detect(packageClass.getPackageName()); } return this; } @@ -328,21 +327,21 @@ public abstract class AbstractHttpServiceRegistrar implements @Override public GroupSpec detectInBasePackages(String... packageNames) { for (String packageName : packageNames) { - detect(this.groupName, this.clientType, packageName); + detect(packageName); } return this; } - private void detect(String groupName, HttpServiceGroup.ClientType clientType, String packageName) { + private void detect(String packageName) { for (BeanDefinition definition : getScanner().findCandidateComponents(packageName)) { if (definition.getBeanClassName() != null) { - getOrCreateGroup(groupName, clientType).addHttpServiceTypeName(definition.getBeanClassName()); + getOrCreateGroup().addHttpServiceTypeName(definition.getBeanClassName()); } } } - private RegisteredGroup getOrCreateGroup(String groupName, HttpServiceGroup.ClientType clientType) { - return groupMap.computeIfAbsent(groupName, name -> new RegisteredGroup(name, clientType)); + private RegisteredGroup getOrCreateGroup() { + return groupMap.computeIfAbsent(this.groupName, name -> new RegisteredGroup(name, this.clientType)); } } } @@ -376,7 +375,7 @@ public abstract class AbstractHttpServiceRegistrar implements @Override public Set> httpServiceTypes() { - return httpServiceTypeNames.stream() + return this.httpServiceTypeNames.stream() .map(AbstractHttpServiceRegistrar::loadClass) .collect(Collectors.toSet()); } diff --git a/spring-web/src/main/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrar.java b/spring-web/src/main/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrar.java index dfd7f38466..418940fd19 100644 --- a/spring-web/src/main/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrar.java +++ b/spring-web/src/main/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrar.java @@ -27,7 +27,7 @@ import org.springframework.core.type.AnnotationMetadata; * @author Rossen Stoyanchev * @since 7.0 */ -final class AnnotationHttpServiceRegistrar extends AbstractHttpServiceRegistrar { +class AnnotationHttpServiceRegistrar extends AbstractHttpServiceRegistrar { @Override protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata importMetadata) { diff --git a/spring-web/src/main/java/org/springframework/web/service/registry/HttpServiceProxyRegistryFactoryBean.java b/spring-web/src/main/java/org/springframework/web/service/registry/HttpServiceProxyRegistryFactoryBean.java index 059c611164..77233855ae 100644 --- a/spring-web/src/main/java/org/springframework/web/service/registry/HttpServiceProxyRegistryFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/web/service/registry/HttpServiceProxyRegistryFactoryBean.java @@ -96,10 +96,10 @@ public final class HttpServiceProxyRegistryFactoryBean Assert.notNull(this.applicationContext, "ApplicationContext not initialized"); // Apply group configurers - this.groupSet.forEach(group -> - this.applicationContext.getBeanProvider(group.getConfigurerType()) - .orderedStream() - .forEach(configurer -> configurer.configureGroups(new DefaultGroups<>(group.clientType())))); + groupAdapters.forEach((clientType, groupAdapter) -> + this.applicationContext.getBeanProvider(groupAdapter.getConfigurerType()) + .orderedStream() + .forEach(configurer -> configurer.configureGroups(new DefaultGroups<>(clientType)))); // Create proxies Map, Object>> groupProxyMap = this.groupSet.stream() @@ -193,13 +193,13 @@ public final class HttpServiceProxyRegistryFactoryBean } public Map, Object> createProxies() { - Map, Object> proxyMap = new LinkedHashMap<>(httpServiceTypes().size()); + Map, Object> map = new LinkedHashMap<>(httpServiceTypes().size()); HttpExchangeAdapter exchangeAdapter = initExchangeAdapter(); HttpServiceProxyFactory.Builder proxyFactoryBuilder = HttpServiceProxyFactory.builderFor(exchangeAdapter); this.proxyFactoryConfigurer.accept(this, proxyFactoryBuilder); - HttpServiceProxyFactory proxyFactory = proxyFactoryBuilder.build(); - httpServiceTypes().forEach(type -> proxyMap.put(type, proxyFactory.createClient(type))); - return proxyMap; + HttpServiceProxyFactory factory = proxyFactoryBuilder.build(); + httpServiceTypes().forEach(type -> map.put(type, factory.createClient(type))); + return map; } @SuppressWarnings("unchecked") @@ -219,12 +219,10 @@ public final class HttpServiceProxyRegistryFactoryBean */ private final class DefaultGroups implements HttpServiceGroupConfigurer.Groups { - private final HttpServiceGroup.ClientType clientType; - - private @Nullable Predicate filter; + private Predicate filter; DefaultGroups(HttpServiceGroup.ClientType clientType) { - this.clientType = clientType; + this.filter = group -> group.clientType().equals(clientType); } @Override @@ -234,7 +232,7 @@ public final class HttpServiceProxyRegistryFactoryBean @Override public HttpServiceGroupConfigurer.Groups filter(Predicate predicate) { - this.filter = (this.filter != null ? this.filter.or(predicate) : predicate); + this.filter = this.filter.or(predicate); return this; } @@ -260,10 +258,8 @@ public final class HttpServiceProxyRegistryFactoryBean BiConsumer clientConfigurer, BiConsumer proxyFactoryConfigurer) { - groupSet.stream() - .filter(group -> group.clientType().equals(this.clientType)) - .filter(groups -> this.filter == null || this.filter.test(groups)) - .forEach(group -> group.apply(clientConfigurer, proxyFactoryConfigurer)); + groupSet.stream().filter(this.filter).forEach(group -> + group.apply(clientConfigurer, proxyFactoryConfigurer)); } } diff --git a/spring-web/src/test/java/org/springframework/web/client/support/RestClientProxyRegistryIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/support/RestClientProxyRegistryIntegrationTests.java index 45d2b088e1..fde053c11d 100644 --- a/spring-web/src/test/java/org/springframework/web/client/support/RestClientProxyRegistryIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/support/RestClientProxyRegistryIntegrationTests.java @@ -31,13 +31,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.web.client.support.echo.EchoA; -import org.springframework.web.client.support.echo.EchoB; -import org.springframework.web.client.support.greeting.GreetingA; -import org.springframework.web.client.support.greeting.GreetingB; import org.springframework.web.service.registry.AbstractHttpServiceRegistrar; import org.springframework.web.service.registry.HttpServiceProxyRegistry; import org.springframework.web.service.registry.ImportHttpServices; +import org.springframework.web.service.registry.echo.EchoA; +import org.springframework.web.service.registry.echo.EchoB; +import org.springframework.web.service.registry.greeting.GreetingA; +import org.springframework.web.service.registry.greeting.GreetingB; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-web/src/test/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrarTests.java b/spring-web/src/test/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrarTests.java new file mode 100644 index 0000000000..f87bbdd1a9 --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrarTests.java @@ -0,0 +1,195 @@ +/* + * Copyright 2002-2025 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. + */ + +package org.springframework.web.service.registry; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.web.service.registry.HttpServiceGroup.ClientType; +import org.springframework.web.service.registry.echo.EchoA; +import org.springframework.web.service.registry.echo.EchoB; +import org.springframework.web.service.registry.greeting.GreetingA; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link AnnotationHttpServiceRegistrar}. + * @author Rossen Stoyanchev + */ +public class AnnotationHttpServiceRegistrarTests { + + private static final String ECHO_GROUP = "echo"; + + private static final String GREETING_GROUP = "greeting"; + + + private final TestGroupRegistry groupRegistry = new TestGroupRegistry(); + + private final TestAnnotationHttpServiceRegistrar registrar = new TestAnnotationHttpServiceRegistrar(); + + + @Test + void basicListing() { + doRegister(ListingConfig.class); + assertGroups(StubGroup.ofListing(ECHO_GROUP, EchoA.class, EchoB.class)); + } + + @Test + void basicScan() { + doRegister(ScanConfig.class); + assertGroups( + StubGroup.ofPackageClasses(ECHO_GROUP, EchoA.class), + StubGroup.ofPackageClasses(GREETING_GROUP, GreetingA.class)); + } + + @Test + void containerWithClientType() { + doRegister(ContainerConfig.class); + assertGroups( + StubGroup.ofListing(ECHO_GROUP, ClientType.WEB_CLIENT, EchoA.class), + StubGroup.ofListing(GREETING_GROUP, ClientType.WEB_CLIENT, GreetingA.class)); + } + + private void doRegister(Class configClass) { + AnnotationMetadata metadata = AnnotationMetadata.introspect(configClass); + this.registrar.registerHttpServices(this.groupRegistry, metadata); + } + + private void assertGroups(StubGroup... expectedGroups) { + Map groupMap = this.groupRegistry.groupMap(); + assertThat(groupMap.size()).isEqualTo(expectedGroups.length); + for (StubGroup expected : expectedGroups) { + StubGroup actual = groupMap.get(expected.name()); + assertThat(actual.httpServiceTypes()).isEqualTo(expected.httpServiceTypes()); + assertThat(actual.clientType()).isEqualTo(expected.clientType()); + assertThat(actual.packageNames()).isEqualTo(expected.packageNames()); + assertThat(actual.packageClasses()).isEqualTo(expected.packageClasses()); + } + } + + + @ImportHttpServices(group = ECHO_GROUP, types = {EchoA.class, EchoB.class}) + private static class ListingConfig { + } + + @ImportHttpServices(group = ECHO_GROUP, basePackageClasses = {EchoA.class}) + @ImportHttpServices(group = GREETING_GROUP, basePackageClasses = {GreetingA.class}) + private static class ScanConfig { + } + + @HttpServiceGroups(clientType = ClientType.WEB_CLIENT, groups = { + @ImportHttpServices(group = ECHO_GROUP, types = {EchoA.class}), + @ImportHttpServices(group = GREETING_GROUP, types = {GreetingA.class}) + }) + private static class ContainerConfig { + } + + + private static class TestAnnotationHttpServiceRegistrar extends AnnotationHttpServiceRegistrar { + + @Override + public void registerHttpServices(GroupRegistry registry, AnnotationMetadata importMetadata) { + super.registerHttpServices(registry, importMetadata); + } + } + + + private static class TestGroupRegistry implements AbstractHttpServiceRegistrar.GroupRegistry { + + private final Map groupMap = new LinkedHashMap<>(); + + public Map groupMap() { + return this.groupMap; + } + + @Override + public GroupSpec forGroup(String name, ClientType clientType) { + return new TestGroupSpec(name, clientType); + } + + private class TestGroupSpec implements GroupSpec { + + private final String groupName; + + private final ClientType clientType; + + public TestGroupSpec(String groupName, ClientType clientType) { + this.groupName = groupName; + this.clientType = clientType; + } + + @Override + public GroupSpec register(Class... serviceTypes) { + getOrCreateGroup().httpServiceTypes().addAll(Arrays.asList(serviceTypes)); + return this; + } + + @Override + public GroupSpec detectInBasePackages(Class... packageClasses) { + getOrCreateGroup().packageClasses().addAll(Arrays.asList(packageClasses)); + return this; + } + + @Override + public GroupSpec detectInBasePackages(String... packageNames) { + getOrCreateGroup().packageNames().addAll(Arrays.asList(packageNames)); + return this; + } + + private StubGroup getOrCreateGroup() { + return groupMap.computeIfAbsent(this.groupName, name -> new StubGroup(name, this.clientType)); + } + } + } + + + private record StubGroup( + String name, ClientType clientType, Set> httpServiceTypes, + Set> packageClasses, Set packageNames) implements HttpServiceGroup { + + StubGroup(String name, ClientType clientType) { + this(name, clientType, new LinkedHashSet<>(), new LinkedHashSet<>(), new LinkedHashSet<>()); + } + + public static StubGroup ofListing(String name, Class... httpServiceTypes) { + return ofListing(name, ClientType.UNSPECIFIED, httpServiceTypes); + } + + public static StubGroup ofListing(String name, ClientType clientType, Class... httpServiceTypes) { + StubGroup group = new StubGroup(name, clientType); + group.httpServiceTypes().addAll(Arrays.asList(httpServiceTypes)); + return group; + } + + public static StubGroup ofPackageClasses(String name, Class... packageClasses) { + return ofPackageClasses(name, ClientType.UNSPECIFIED, packageClasses); + } + + public static StubGroup ofPackageClasses(String name, ClientType clientType, Class... packageClasses) { + StubGroup group = new StubGroup(name, clientType); + group.packageClasses().addAll(Arrays.asList(packageClasses)); + return group; + } + } + +} diff --git a/spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceRegistrarTests.java b/spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceRegistrarTests.java new file mode 100644 index 0000000000..0a09efd78c --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceRegistrarTests.java @@ -0,0 +1,194 @@ +/* + * Copyright 2002-2025 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. + */ + +package org.springframework.web.service.registry; + +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.web.service.registry.HttpServiceGroup.ClientType; +import org.springframework.web.service.registry.echo.EchoA; +import org.springframework.web.service.registry.echo.EchoB; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Unit tests for {@link AbstractHttpServiceRegistrar}. + * @author Rossen Stoyanchev + */ +@SuppressWarnings("unchecked") +public class HttpServiceRegistrarTests { + + private static final String ECHO_GROUP = "echo"; + + + private final SimpleBeanDefinitionRegistry beanDefRegistry = new SimpleBeanDefinitionRegistry(); + + + @Test + void basicListing() { + doRegister(registry -> registry.forGroup(ECHO_GROUP).register(EchoA.class, EchoB.class)); + + assertRegistryBeanDef(new TestGroup(ECHO_GROUP, EchoA.class, EchoB.class)); + assertProxyBeanDef(ECHO_GROUP, EchoA.class); + assertProxyBeanDef(ECHO_GROUP, EchoB.class); + assertBeanDefinitionCount(3); + } + + @Test + void basicScan() { + doRegister(registry -> registry.forGroup(ECHO_GROUP).detectInBasePackages(EchoA.class)); + + assertRegistryBeanDef(new TestGroup(ECHO_GROUP, EchoA.class, EchoB.class)); + assertProxyBeanDef(ECHO_GROUP, EchoA.class); + assertProxyBeanDef(ECHO_GROUP, EchoB.class); + assertBeanDefinitionCount(3); + } + + @Test + void merge() { + doRegister( + registry -> registry.forGroup(ECHO_GROUP).register(EchoA.class), + registry -> registry.forGroup(ECHO_GROUP).register(EchoB.class)); + + assertRegistryBeanDef(new TestGroup(ECHO_GROUP, EchoA.class, EchoB.class)); + assertProxyBeanDef(ECHO_GROUP, EchoA.class); + assertProxyBeanDef(ECHO_GROUP, EchoB.class); + assertBeanDefinitionCount(3); + } + + @Test + void mergeWithOverlap() { + doRegister( + registry -> registry.forGroup(ECHO_GROUP).register(EchoA.class), + registry -> registry.forGroup(ECHO_GROUP).register(EchoA.class)); + + assertRegistryBeanDef(new TestGroup(ECHO_GROUP, EchoA.class)); + assertProxyBeanDef(ECHO_GROUP, EchoA.class); + assertBeanDefinitionCount(2); + } + + @Test + void mergeWithClientTypeConflict() { + assertThatIllegalArgumentException().isThrownBy(() -> doRegister( + registry -> registry.forGroup(ECHO_GROUP, ClientType.REST_CLIENT).register(EchoA.class), + registry -> registry.forGroup(ECHO_GROUP, ClientType.WEB_CLIENT).register(EchoB.class))); + } + + @Test + void defaultClientType() { + doRegister(ClientType.WEB_CLIENT, registry -> registry.forGroup(ECHO_GROUP).register(EchoA.class)); + assertRegistryBeanDef(new TestGroup(ECHO_GROUP, ClientType.WEB_CLIENT, EchoA.class)); + } + + @Test + void noRegistrations() { + doRegister(registry -> {}); + assertRegistryBeanDef(); + assertBeanDefinitionCount(1); + } + + + @SuppressWarnings("unchecked") + private void doRegister(Consumer... registrars) { + doRegister(ClientType.UNSPECIFIED, registrars); + } + + @SuppressWarnings("DataFlowIssue") + private void doRegister(ClientType clientType, Consumer... consumers) { + for (Consumer consumer : consumers) { + TestRegistrar registrar = new TestRegistrar(consumer, clientType); + registrar.registerBeanDefinitions(null, beanDefRegistry); + } + } + + private void assertRegistryBeanDef(HttpServiceGroup... expectedGroups) { + Map groupMap = groupMap(); + assertThat(groupMap.size()).isEqualTo(expectedGroups.length); + for (HttpServiceGroup expected : expectedGroups) { + HttpServiceGroup actual = groupMap.get(expected.name()); + assertThat(actual.httpServiceTypes()).isEqualTo(expected.httpServiceTypes()); + assertThat(actual.clientType()).isEqualTo(expected.clientType()); + } + } + + @SuppressWarnings("unchecked") + private Map groupMap() { + BeanDefinition beanDef = this.beanDefRegistry.getBeanDefinition("httpServiceProxyRegistry"); + assertThat(beanDef.getBeanClassName()).isEqualTo(HttpServiceProxyRegistryFactoryBean.class.getName()); + + ConstructorArgumentValues args = beanDef.getConstructorArgumentValues(); + ConstructorArgumentValues.ValueHolder valueHolder = args.getArgumentValue(0, Map.class); + assertThat(valueHolder).isNotNull(); + + Map groupMap = (Map) valueHolder.getValue(); + assertThat(groupMap).isNotNull(); + + return groupMap; + } + + private void assertProxyBeanDef(String group, Class httpServiceType) { + String beanName = group + "#" + httpServiceType.getName(); + assertThat(this.beanDefRegistry.containsBeanDefinition(beanName)).isTrue(); + BeanDefinition beanDef = this.beanDefRegistry.getBeanDefinition(beanName); + assertThat(beanDef.getBeanClassName()).isEqualTo(httpServiceType.getName()); + + } + + private void assertBeanDefinitionCount(int count) { + assertThat(beanDefRegistry.getBeanDefinitionCount()).isEqualTo(count); + } + + + private static class TestRegistrar extends AbstractHttpServiceRegistrar { + + private final Consumer registrar; + + TestRegistrar(Consumer registrar, ClientType clientType) { + this.registrar = registrar; + setDefaultClientType(clientType); + setEnvironment(new StandardEnvironment()); + setResourceLoader(new PathMatchingResourcePatternResolver()); + } + + @Override + protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata metadata) { + this.registrar.accept(registry); + } + } + + private record TestGroup(String name, Set> httpServiceTypes, ClientType clientType) + implements HttpServiceGroup { + + TestGroup(String name, Class... httpServiceTypes) { + this(name, Set.of(httpServiceTypes), ClientType.REST_CLIENT); + } + + TestGroup(String name, ClientType clientType, Class... httpServiceTypes) { + this(name, Set.of(httpServiceTypes), clientType); + } + } +} diff --git a/spring-web/src/test/java/org/springframework/web/client/support/echo/EchoA.java b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoA.java similarity index 93% rename from spring-web/src/test/java/org/springframework/web/client/support/echo/EchoA.java rename to spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoA.java index 8a11d1b27a..cb2da719c6 100644 --- a/spring-web/src/test/java/org/springframework/web/client/support/echo/EchoA.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoA.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.client.support.echo; +package org.springframework.web.service.registry.echo; import org.springframework.web.bind.annotation.RequestParam; diff --git a/spring-web/src/test/java/org/springframework/web/client/support/echo/EchoB.java b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoB.java similarity index 93% rename from spring-web/src/test/java/org/springframework/web/client/support/echo/EchoB.java rename to spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoB.java index 882548458b..5db1e1a0fb 100644 --- a/spring-web/src/test/java/org/springframework/web/client/support/echo/EchoB.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoB.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.client.support.echo; +package org.springframework.web.service.registry.echo; import org.springframework.web.bind.annotation.RequestParam; diff --git a/spring-web/src/test/java/org/springframework/web/client/support/greeting/GreetingA.java b/spring-web/src/test/java/org/springframework/web/service/registry/greeting/GreetingA.java similarity index 93% rename from spring-web/src/test/java/org/springframework/web/client/support/greeting/GreetingA.java rename to spring-web/src/test/java/org/springframework/web/service/registry/greeting/GreetingA.java index 7b4385e127..ba01bd72ed 100644 --- a/spring-web/src/test/java/org/springframework/web/client/support/greeting/GreetingA.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/greeting/GreetingA.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.client.support.greeting; +package org.springframework.web.service.registry.greeting; import org.springframework.web.bind.annotation.RequestParam; diff --git a/spring-web/src/test/java/org/springframework/web/client/support/greeting/GreetingB.java b/spring-web/src/test/java/org/springframework/web/service/registry/greeting/GreetingB.java similarity index 93% rename from spring-web/src/test/java/org/springframework/web/client/support/greeting/GreetingB.java rename to spring-web/src/test/java/org/springframework/web/service/registry/greeting/GreetingB.java index c1dfcb53a9..f37a9fc112 100644 --- a/spring-web/src/test/java/org/springframework/web/client/support/greeting/GreetingB.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/greeting/GreetingB.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.client.support.greeting; +package org.springframework.web.service.registry.greeting; import org.springframework.web.bind.annotation.RequestParam;