Skip to content

Commit cacaf31

Browse files
committed
Add provider variant of GroupRegistry.forGroup
Update `GroupRegistry` with an alternative `forGroup` method that uses provider functions for the group name and client type. This update allows more advanced group registration, for example where the group name is provided is obtained from an annotation of the HTTP Service type. It also allows registration to be skipped by returning `null` as the group name.
1 parent 4bb191d commit cacaf31

File tree

3 files changed

+84
-22
lines changed

3 files changed

+84
-22
lines changed

spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.Arrays;
2020
import java.util.Objects;
21+
import java.util.function.Function;
2122

2223
import org.jspecify.annotations.Nullable;
2324

@@ -44,6 +45,7 @@
4445
import org.springframework.util.Assert;
4546
import org.springframework.util.ClassUtils;
4647
import org.springframework.web.service.annotation.HttpExchange;
48+
import org.springframework.web.service.registry.HttpServiceGroup.ClientType;
4749

4850
/**
4951
* Abstract registrar class that imports:
@@ -226,9 +228,21 @@ default GroupSpec forGroup(String name) {
226228
}
227229

228230
/**
229-
* Variant of {@link #forGroup(String)} with a client type.
231+
* Perform HTTP Service registrations for the given group and client type.
230232
*/
231-
GroupSpec forGroup(String name, HttpServiceGroup.ClientType clientType);
233+
default GroupSpec forGroup(String name, HttpServiceGroup.ClientType clientType) {
234+
return forGroup(serviceType -> name, serviceType -> clientType);
235+
}
236+
237+
/**
238+
* Perform HTTP Service registrations for the given group and client type.
239+
* @param nameProvider a function that will provide the name given a service type
240+
* or return {@code null} if the HTTP service should not be registered
241+
* @param clientTypeProvider a function that will provide the client type given a
242+
* service type
243+
*/
244+
GroupSpec forGroup(Function<Class<?>, @Nullable String> nameProvider,
245+
Function<Class<?>, HttpServiceGroup.ClientType> clientTypeProvider);
232246

233247
/**
234248
* Perform HTTP Service registrations for the
@@ -269,22 +283,28 @@ interface GroupSpec {
269283
private class DefaultGroupRegistry implements GroupRegistry {
270284

271285
@Override
272-
public GroupSpec forGroup(String name, HttpServiceGroup.ClientType clientType) {
273-
return new DefaultGroupSpec(name, clientType);
286+
public GroupSpec forGroup(Function<Class<?>, @Nullable String> nameProvider,
287+
Function<Class<?>, ClientType> clientTypeProvider) {
288+
289+
return new DefaultGroupSpec(nameProvider, clientTypeProvider);
274290
}
275291

276292
private class DefaultGroupSpec implements GroupSpec {
277293

278-
private final GroupsMetadata.Registration registration;
294+
private final Function<Class<?>, @Nullable String> groupNameProvider;
295+
296+
private final Function<Class<?>, ClientType> clientTypeProvider;
279297

280-
DefaultGroupSpec(String groupName, HttpServiceGroup.ClientType clientType) {
281-
clientType = (clientType != HttpServiceGroup.ClientType.UNSPECIFIED ? clientType : defaultClientType);
282-
this.registration = groupsMetadata.getOrCreateGroup(groupName, clientType);
298+
DefaultGroupSpec(Function<Class<?>, @Nullable String> groupNameProvider,
299+
Function<Class<?>, ClientType> clientTypeProvider) {
300+
301+
this.groupNameProvider = groupNameProvider;
302+
this.clientTypeProvider = clientTypeProvider;
283303
}
284304

285305
@Override
286306
public GroupRegistry.GroupSpec register(Class<?>... serviceTypes) {
287-
Arrays.stream(serviceTypes).map(Class::getName).forEach(this::registerServiceTypeName);
307+
Arrays.stream(serviceTypes).forEach(this::registerServiceType);
288308
return this;
289309
}
290310

@@ -304,11 +324,19 @@ private void detectInBasePackage(String packageName) {
304324
getScanner().findCandidateComponents(packageName).stream()
305325
.map(BeanDefinition::getBeanClassName)
306326
.filter(Objects::nonNull)
307-
.forEach(this::registerServiceTypeName);
327+
.map(serviceTypeName -> ClassUtils.resolveClassName(serviceTypeName, beanClassLoader))
328+
.forEach(this::registerServiceType);
308329
}
309330

310-
private void registerServiceTypeName(String httpServiceTypeName) {
311-
this.registration.httpServiceTypeNames().add(httpServiceTypeName);
331+
private void registerServiceType(Class<?> httpServiceType) {
332+
String groupName = this.groupNameProvider.apply(httpServiceType);
333+
if (groupName != null) {
334+
ClientType clientType = this.clientTypeProvider.apply(httpServiceType);
335+
clientType = (clientType != HttpServiceGroup.ClientType.UNSPECIFIED ? clientType : defaultClientType);
336+
groupsMetadata.getOrCreateGroup(groupName, clientType)
337+
.httpServiceTypeNames()
338+
.add(httpServiceType.getName());
339+
}
312340
}
313341
}
314342
}

spring-web/src/test/java/org/springframework/web/service/registry/AnnotationHttpServiceRegistrarTests.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323
import java.util.Set;
2424
import java.util.function.BiConsumer;
25+
import java.util.function.Function;
2526

2627
import org.junit.jupiter.api.Test;
2728

@@ -179,13 +180,14 @@ public Map<String, StubGroup> groupMap() {
179180
}
180181

181182
@Override
182-
public GroupSpec forGroup(String name, ClientType clientType) {
183-
return new TestGroupSpec(this.groupMap, name, clientType);
183+
public GroupSpec forGroup(Function<Class<?>, String> nameProvider,
184+
Function<Class<?>, ClientType> clientTypeProvider) {
185+
return new TestGroupSpec(this.groupMap, nameProvider, clientTypeProvider);
184186
}
185187

186188

187-
private record TestGroupSpec(Map<String, StubGroup> groupMap, String groupName,
188-
ClientType clientType) implements GroupSpec {
189+
private record TestGroupSpec(Map<String, StubGroup> groupMap, Function<Class<?>, String> groupNameProvider,
190+
Function<Class<?>, ClientType> clientTypeProvider) implements GroupSpec {
189191

190192
@Override
191193
public GroupSpec register(Class<?>... serviceTypes) {
@@ -206,7 +208,9 @@ public GroupSpec detectInBasePackages(String... packageNames) {
206208
}
207209

208210
private StubGroup getOrCreateGroup() {
209-
return this.groupMap.computeIfAbsent(this.groupName, name -> new StubGroup(name, this.clientType));
211+
String groupName = this.groupNameProvider.apply(Object.class);
212+
ClientType clientType = this.clientTypeProvider.apply(Object.class);
213+
return this.groupMap.computeIfAbsent(groupName, name -> new StubGroup(name, clientType));
210214
}
211215
}
212216
}

spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceRegistrarTests.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,33 @@ void basicScan() {
7070
assertBeanDefinitionCount(3);
7171
}
7272

73+
@Test
74+
void scanWithProviders() {
75+
doRegister(registry -> registry
76+
.forGroup(type -> type.getName().substring(type.getName().length() - 1),
77+
type -> type.getName().endsWith("B") ? ClientType.REST_CLIENT : ClientType.UNSPECIFIED)
78+
.detectInBasePackages(EchoA.class));
79+
assertRegistryBeanDef(new TestGroup("A", EchoA.class), new TestGroup("B", EchoB.class));
80+
assertProxyBeanDef("A", EchoA.class);
81+
assertProxyBeanDef("B", EchoB.class);
82+
assertBeanDefinitionCount(3);
83+
GroupsMetadata groupsMetadata = groupsMetadata();
84+
assertThat(getRegistration(groupsMetadata, "A").clientType()).isEqualTo(ClientType.UNSPECIFIED);
85+
assertThat(getRegistration(groupsMetadata, "B").clientType()).isEqualTo(ClientType.REST_CLIENT);
86+
}
87+
88+
@Test
89+
void scanWithProvidersWhenProviderReturnsNull() {
90+
doRegister(registry -> registry
91+
.forGroup(type -> type.getName().endsWith("A") ? null : ECHO_GROUP, type -> ClientType.UNSPECIFIED)
92+
.detectInBasePackages(EchoA.class));
93+
assertRegistryBeanDef(new TestGroup(ECHO_GROUP, EchoB.class));
94+
}
95+
96+
private GroupsMetadata.Registration getRegistration(GroupsMetadata groupsMetadata, String name) {
97+
return groupsMetadata.registrations().filter(candidate -> candidate.name().equals(name)).findFirst().get();
98+
}
99+
73100
@Test
74101
void merge() {
75102
doRegister(
@@ -149,18 +176,21 @@ private void assertRegistryBeanDef(HttpServiceGroup... expectedGroups) {
149176
}
150177

151178
private Map<String, HttpServiceGroup> groupMap() {
179+
GroupsMetadata metadata = groupsMetadata();
180+
assertThat(metadata).isNotNull();
181+
return metadata.groups(null).stream()
182+
.collect(Collectors.toMap(HttpServiceGroup::name, Function.identity()));
183+
}
184+
185+
private GroupsMetadata groupsMetadata() {
152186
BeanDefinition beanDef = this.beanDefRegistry.getBeanDefinition(AbstractHttpServiceRegistrar.HTTP_SERVICE_PROXY_REGISTRY_BEAN_NAME);
153187
assertThat(beanDef.getBeanClassName()).isEqualTo(HttpServiceProxyRegistryFactoryBean.class.getName());
154188

155189
ConstructorArgumentValues args = beanDef.getConstructorArgumentValues();
156190
ConstructorArgumentValues.ValueHolder valueHolder = args.getArgumentValue(0, Map.class);
157191
assertThat(valueHolder).isNotNull();
158192

159-
GroupsMetadata metadata = (GroupsMetadata) valueHolder.getValue();
160-
assertThat(metadata).isNotNull();
161-
162-
return metadata.groups(null).stream()
163-
.collect(Collectors.toMap(HttpServiceGroup::name, Function.identity()));
193+
return (GroupsMetadata) valueHolder.getValue();
164194
}
165195

166196
private void assertProxyBeanDef(String group, Class<?> httpServiceType) {

0 commit comments

Comments
 (0)