Skip to content

Add provider variant of GroupRegistry.forGroup #35234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;

import org.jspecify.annotations.Nullable;

Expand All @@ -44,6 +45,7 @@
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.service.registry.HttpServiceGroup.ClientType;

/**
* Abstract registrar class that imports:
Expand Down Expand Up @@ -226,9 +228,21 @@ default GroupSpec forGroup(String name) {
}

/**
* Variant of {@link #forGroup(String)} with a client type.
* Perform HTTP Service registrations for the given group and client type.
*/
GroupSpec forGroup(String name, HttpServiceGroup.ClientType clientType);
default GroupSpec forGroup(String name, HttpServiceGroup.ClientType clientType) {
return forGroup(serviceType -> name, serviceType -> clientType);
}

/**
* Perform HTTP Service registrations for the given group and client type.
* @param nameProvider a function that will provide the name given a service type
* or return {@code null} if the HTTP service should not be registered
* @param clientTypeProvider a function that will provide the client type given a
* service type
*/
GroupSpec forGroup(Function<Class<?>, @Nullable String> nameProvider,
Function<Class<?>, HttpServiceGroup.ClientType> clientTypeProvider);

/**
* Perform HTTP Service registrations for the
Expand Down Expand Up @@ -269,22 +283,28 @@ interface GroupSpec {
private class DefaultGroupRegistry implements GroupRegistry {

@Override
public GroupSpec forGroup(String name, HttpServiceGroup.ClientType clientType) {
return new DefaultGroupSpec(name, clientType);
public GroupSpec forGroup(Function<Class<?>, @Nullable String> nameProvider,
Function<Class<?>, ClientType> clientTypeProvider) {

return new DefaultGroupSpec(nameProvider, clientTypeProvider);
}

private class DefaultGroupSpec implements GroupSpec {

private final GroupsMetadata.Registration registration;
private final Function<Class<?>, @Nullable String> groupNameProvider;

private final Function<Class<?>, ClientType> clientTypeProvider;

DefaultGroupSpec(String groupName, HttpServiceGroup.ClientType clientType) {
clientType = (clientType != HttpServiceGroup.ClientType.UNSPECIFIED ? clientType : defaultClientType);
this.registration = groupsMetadata.getOrCreateGroup(groupName, clientType);
DefaultGroupSpec(Function<Class<?>, @Nullable String> groupNameProvider,
Function<Class<?>, ClientType> clientTypeProvider) {

this.groupNameProvider = groupNameProvider;
this.clientTypeProvider = clientTypeProvider;
}

@Override
public GroupRegistry.GroupSpec register(Class<?>... serviceTypes) {
Arrays.stream(serviceTypes).map(Class::getName).forEach(this::registerServiceTypeName);
Arrays.stream(serviceTypes).forEach(this::registerServiceType);
return this;
}

Expand All @@ -304,11 +324,19 @@ private void detectInBasePackage(String packageName) {
getScanner().findCandidateComponents(packageName).stream()
.map(BeanDefinition::getBeanClassName)
.filter(Objects::nonNull)
.forEach(this::registerServiceTypeName);
.map(serviceTypeName -> ClassUtils.resolveClassName(serviceTypeName, beanClassLoader))
.forEach(this::registerServiceType);
}

private void registerServiceTypeName(String httpServiceTypeName) {
this.registration.httpServiceTypeNames().add(httpServiceTypeName);
private void registerServiceType(Class<?> httpServiceType) {
String groupName = this.groupNameProvider.apply(httpServiceType);
if (groupName != null) {
ClientType clientType = this.clientTypeProvider.apply(httpServiceType);
clientType = (clientType != HttpServiceGroup.ClientType.UNSPECIFIED ? clientType : defaultClientType);
groupsMetadata.getOrCreateGroup(groupName, clientType)
.httpServiceTypeNames()
.add(httpServiceType.getName());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;

import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -179,13 +180,14 @@ public Map<String, StubGroup> groupMap() {
}

@Override
public GroupSpec forGroup(String name, ClientType clientType) {
return new TestGroupSpec(this.groupMap, name, clientType);
public GroupSpec forGroup(Function<Class<?>, String> nameProvider,
Function<Class<?>, ClientType> clientTypeProvider) {
return new TestGroupSpec(this.groupMap, nameProvider, clientTypeProvider);
}


private record TestGroupSpec(Map<String, StubGroup> groupMap, String groupName,
ClientType clientType) implements GroupSpec {
private record TestGroupSpec(Map<String, StubGroup> groupMap, Function<Class<?>, String> groupNameProvider,
Function<Class<?>, ClientType> clientTypeProvider) implements GroupSpec {

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

private StubGroup getOrCreateGroup() {
return this.groupMap.computeIfAbsent(this.groupName, name -> new StubGroup(name, this.clientType));
String groupName = this.groupNameProvider.apply(Object.class);
ClientType clientType = this.clientTypeProvider.apply(Object.class);
return this.groupMap.computeIfAbsent(groupName, name -> new StubGroup(name, clientType));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,33 @@ void basicScan() {
assertBeanDefinitionCount(3);
}

@Test
void scanWithProviders() {
doRegister(registry -> registry
.forGroup(type -> type.getName().substring(type.getName().length() - 1),
type -> type.getName().endsWith("B") ? ClientType.REST_CLIENT : ClientType.UNSPECIFIED)
.detectInBasePackages(EchoA.class));
assertRegistryBeanDef(new TestGroup("A", EchoA.class), new TestGroup("B", EchoB.class));
assertProxyBeanDef("A", EchoA.class);
assertProxyBeanDef("B", EchoB.class);
assertBeanDefinitionCount(3);
GroupsMetadata groupsMetadata = groupsMetadata();
assertThat(getRegistration(groupsMetadata, "A").clientType()).isEqualTo(ClientType.UNSPECIFIED);
assertThat(getRegistration(groupsMetadata, "B").clientType()).isEqualTo(ClientType.REST_CLIENT);
}

@Test
void scanWithProvidersWhenProviderReturnsNull() {
doRegister(registry -> registry
.forGroup(type -> type.getName().endsWith("A") ? null : ECHO_GROUP, type -> ClientType.UNSPECIFIED)
.detectInBasePackages(EchoA.class));
assertRegistryBeanDef(new TestGroup(ECHO_GROUP, EchoB.class));
}

private GroupsMetadata.Registration getRegistration(GroupsMetadata groupsMetadata, String name) {
return groupsMetadata.registrations().filter(candidate -> candidate.name().equals(name)).findFirst().get();
}

@Test
void merge() {
doRegister(
Expand Down Expand Up @@ -149,18 +176,21 @@ private void assertRegistryBeanDef(HttpServiceGroup... expectedGroups) {
}

private Map<String, HttpServiceGroup> groupMap() {
GroupsMetadata metadata = groupsMetadata();
assertThat(metadata).isNotNull();
return metadata.groups(null).stream()
.collect(Collectors.toMap(HttpServiceGroup::name, Function.identity()));
}

private GroupsMetadata groupsMetadata() {
BeanDefinition beanDef = this.beanDefRegistry.getBeanDefinition(AbstractHttpServiceRegistrar.HTTP_SERVICE_PROXY_REGISTRY_BEAN_NAME);
assertThat(beanDef.getBeanClassName()).isEqualTo(HttpServiceProxyRegistryFactoryBean.class.getName());

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

GroupsMetadata metadata = (GroupsMetadata) valueHolder.getValue();
assertThat(metadata).isNotNull();

return metadata.groups(null).stream()
.collect(Collectors.toMap(HttpServiceGroup::name, Function.identity()));
return (GroupsMetadata) valueHolder.getValue();
}

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