diff --git a/samples/grpc-oauth2/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java b/samples/grpc-oauth2/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java index 5c91527..90646fa 100644 --- a/samples/grpc-oauth2/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java +++ b/samples/grpc-oauth2/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java @@ -1,15 +1,11 @@ package org.springframework.grpc.sample; -import io.grpc.Status.Code; -import io.grpc.StatusRuntimeException; -import io.grpc.reflection.v1.ServerReflectionGrpc; -import io.grpc.reflection.v1.ServerReflectionRequest; -import io.grpc.reflection.v1.ServerReflectionResponse; -import io.grpc.stub.StreamObserver; +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; + import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.ObjectProvider; @@ -23,8 +19,7 @@ import org.springframework.experimental.boot.server.exec.CommonsExecWebServerFac import org.springframework.experimental.boot.server.exec.MavenClasspathEntry; import org.springframework.experimental.boot.test.context.EnableDynamicProperty; import org.springframework.experimental.boot.test.context.OAuth2ClientProviderIssuerUri; -import org.springframework.grpc.client.ChannelBuilderOptions; -import org.springframework.grpc.client.GrpcClientFactoryCustomizer; +import org.springframework.grpc.client.GrpcChannelBuilderCustomizer; import org.springframework.grpc.client.ImportGrpcClients; import org.springframework.grpc.client.interceptor.security.BearerTokenAuthenticationInterceptor; import org.springframework.grpc.sample.proto.HelloReply; @@ -36,8 +31,12 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.test.annotation.DirtiesContext; -import static org.junit.Assert.assertThrows; -import static org.junit.jupiter.api.Assertions.assertEquals; +import io.grpc.Status.Code; +import io.grpc.StatusRuntimeException; +import io.grpc.reflection.v1.ServerReflectionGrpc; +import io.grpc.reflection.v1.ServerReflectionRequest; +import io.grpc.reflection.v1.ServerReflectionResponse; +import io.grpc.stub.StreamObserver; @SpringBootTest(properties = { "spring.grpc.server.port=0", "spring.grpc.client.default-channel.address=static://0.0.0.0:${local.grpc.port}" }) @@ -129,9 +128,9 @@ public class GrpcServerApplicationTests { } @Bean - GrpcClientFactoryCustomizer stubs(ObjectProvider context) { - return registry -> registry.channel("secure", ChannelBuilderOptions.defaults() - .withInterceptors(List.of(new BearerTokenAuthenticationInterceptor(() -> token(context))))); + GrpcChannelBuilderCustomizer stubs(ObjectProvider context) { + return GrpcChannelBuilderCustomizer.matching("secure", + builder -> builder.intercept(new BearerTokenAuthenticationInterceptor(() -> token(context)))); } private String token(ObjectProvider context) { diff --git a/samples/grpc-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java b/samples/grpc-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java index a29fdda..975d659 100644 --- a/samples/grpc-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java +++ b/samples/grpc-secure/src/test/java/org/springframework/grpc/sample/GrpcServerApplicationTests.java @@ -3,7 +3,6 @@ package org.springframework.grpc.sample; import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -15,8 +14,7 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.grpc.client.ChannelBuilderOptions; -import org.springframework.grpc.client.GrpcClientFactoryCustomizer; +import org.springframework.grpc.client.GrpcChannelBuilderCustomizer; import org.springframework.grpc.client.ImportGrpcClients; import org.springframework.grpc.client.interceptor.security.BasicAuthenticationInterceptor; import org.springframework.grpc.sample.proto.HelloReply; @@ -113,9 +111,9 @@ public class GrpcServerApplicationTests { static class ExtraConfiguration { @Bean - GrpcClientFactoryCustomizer basicStubs() { - return registry -> registry.channel("secure", ChannelBuilderOptions.defaults() - .withInterceptors(List.of(new BasicAuthenticationInterceptor("user", "user")))); + GrpcChannelBuilderCustomizer basicStubsCustomizer() { + return GrpcChannelBuilderCustomizer.matching("secure", + builder -> builder.intercept(new BasicAuthenticationInterceptor("user", "user"))); } } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcChannelBuilderCustomizer.java b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcChannelBuilderCustomizer.java index c23901d..baf8b80 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcChannelBuilderCustomizer.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcChannelBuilderCustomizer.java @@ -16,6 +16,8 @@ package org.springframework.grpc.client; +import java.util.function.Consumer; + import io.grpc.ManagedChannelBuilder; /** @@ -54,4 +56,13 @@ public interface GrpcChannelBuilderCustomizer }; } + static > GrpcChannelBuilderCustomizer matching(String pattern, + Consumer> consumer) { + return (authority, channel) -> { + if (pattern.matches(authority)) { + consumer.accept(channel); + } + }; + } + } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactory.java b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactory.java index e5a07a5..c6aa1b7 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactory.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactory.java @@ -18,14 +18,12 @@ package org.springframework.grpc.client; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; @@ -42,14 +40,11 @@ import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; -import io.grpc.ManagedChannel; import io.grpc.stub.AbstractStub; /** * A factory of gRPC clients that can be used to create client stubs as beans in an - * application context. The best way to interact with the factory is to declare a bean of - * type {@link GrpcClientFactoryCustomizer} in the application context. The customizer - * will be called with the factory before it is used to create the beans. + * application context. * * @author Dave Syer */ @@ -63,8 +58,6 @@ public class GrpcClientFactory { private final ApplicationContext context; - private Map> options = new HashMap<>(); - static { DEFAULT_FACTORIES.add((Class>) BlockingStubFactory.class); DEFAULT_FACTORIES.add((Class>) BlockingV2StubFactory.class); @@ -80,25 +73,10 @@ public class GrpcClientFactory { public T getClient(String target, Class type, Class factory) { @SuppressWarnings("unchecked") StubFactory stubs = (StubFactory) findFactory(factory, type); - Supplier channel = this.options.get(target); - if (channel == null) { - channel = () -> channels().createChannel(target, ChannelBuilderOptions.defaults()); - } - Supplier finalChannel = channel; - T client = (T) stubs.create(() -> finalChannel.get(), type); + T client = (T) stubs.create(() -> channels().createChannel(target, ChannelBuilderOptions.defaults()), type); return client; } - /** - * Register a channel factory for the given target. The channel will be created using - * the given options. If no options are provided, the default options will be used. - * @param target the name (or base url) of the target - * @param options the options to use to create the channel - */ - public void channel(String target, ChannelBuilderOptions options) { - this.options.put(target, () -> channels().createChannel(target, options)); - } - private StubFactory findFactory(Class factoryType, Class type) { if (this.factories.isEmpty()) { List> factories = new ArrayList<>(); diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactoryCustomizer.java b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactoryCustomizer.java deleted file mode 100644 index 72569c0..0000000 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactoryCustomizer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024-2024 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.grpc.client; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.context.ApplicationContext; - -/** - * Callback interface that can be implemented by beans wishing to customize the - * {@link GrpcClientFactory} before it is used. The registry is used by the application - * context very early in its lifecycle, so customizers should not refer directly to other - * beans. It is better to use a lazy lookup via the {@link ApplicationContext} or an - * {@link ObjectProvider} - * - * @author Dave Syer - */ -public interface GrpcClientFactoryCustomizer { - - void customize(GrpcClientFactory registry); - -} diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactoryPostProcessor.java b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactoryPostProcessor.java index df307eb..890335c 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactoryPostProcessor.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/client/GrpcClientFactoryPostProcessor.java @@ -15,14 +15,9 @@ */ package org.springframework.grpc.client; -import java.util.ArrayList; -import java.util.List; - import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; /** * Post processor for {@link GrpcClientFactory} that applies the customizers and provides @@ -44,12 +39,6 @@ public class GrpcClientFactoryPostProcessor implements ApplicationContextAware { } this.initialized = true; this.registry = new GrpcClientFactory(context); - if (context.getBeanNamesForType(GrpcClientFactoryCustomizer.class).length > 0) { - List values = new ArrayList<>( - context.getBeansOfType(GrpcClientFactoryCustomizer.class).values()); - AnnotationAwareOrderComparator.sort(values); - values.forEach(customizer -> customizer.customize(this.registry)); - } } T getClient(String target, Class type, Class factory) { @@ -59,12 +48,7 @@ public class GrpcClientFactoryPostProcessor implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - if (applicationContext instanceof GenericApplicationContext generic) { - this.context = generic; - } - else { - throw new IllegalStateException("ApplicationContext must be a GenericApplicationContext"); - } + this.context = applicationContext; } } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/client/ImportGrpcClients.java b/spring-grpc-core/src/main/java/org/springframework/grpc/client/ImportGrpcClients.java index 730d7f8..43b6a02 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/client/ImportGrpcClients.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/client/ImportGrpcClients.java @@ -25,9 +25,7 @@ import java.lang.annotation.Target; import org.springframework.context.annotation.Import; /** - * Annotation to create gRPC client beans. If you want more control over the creation of - * the clients, or you don't want to use the annotation, you can use a bean of type - * {@link GrpcClientFactoryCustomizer} instead. + * Annotation to create gRPC client beans. * * @author Dave Syer */