diff --git a/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc b/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc index caa58de..7d22345 100644 --- a/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc +++ b/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc @@ -18,6 +18,7 @@ |spring.grpc.client.default-channel.ssl.bundle | | SSL bundle name. |spring.grpc.client.default-channel.ssl.enabled | | Whether to enable SSL support. Enabled automatically if "bundle" is provided unless specified otherwise. |spring.grpc.client.default-channel.user-agent | | The custom User-Agent for the channel. +|spring.grpc.client.enabled | `+++true+++` | Whether to enable client autoconfiguration. |spring.grpc.client.observations.enabled | `+++true+++` | Whether to enable Observations on the client. |spring.grpc.server.address | | The address to bind to. could be a host:port combination or a pseudo URL like static://host:port. Can not be set if host or port are set independently. |spring.grpc.server.exception-handling.enabled | `+++true+++` | Whether to enable user-defined global exception handling on the gRPC server. diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/ConditionalOnGrpcClientEnabled.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/ConditionalOnGrpcClientEnabled.java new file mode 100644 index 0000000..75060f8 --- /dev/null +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/ConditionalOnGrpcClientEnabled.java @@ -0,0 +1,41 @@ +/* + * Copyright 2023-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.autoconfigure.client; + +import io.grpc.stub.AbstractStub; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Conditional; + +/** + * {@link Conditional @Conditional} that only matches when the {@code io.grpc:grpc-stub} + * module is in the classpath and the {@code spring.grpc.client.enabled} property is not + * explicitly set to {@code false}. + * + * @author Freeman + * @author Chris Bono + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@ConditionalOnClass(AbstractStub.class) +@ConditionalOnProperty(prefix = "spring.grpc.client", name = "enabled", matchIfMissing = true) +public @interface ConditionalOnGrpcClientEnabled { + +} diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfiguration.java index d023142..5f0d8a7 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfiguration.java @@ -32,6 +32,7 @@ import io.grpc.DecompressorRegistry; import io.grpc.ManagedChannelBuilder; @AutoConfiguration +@ConditionalOnGrpcClientEnabled @EnableConfigurationProperties(GrpcClientProperties.class) @Import({ GrpcCodecConfiguration.class, ClientInterceptorsConfiguration.class, GrpcChannelFactoryConfigurations.ShadedNettyChannelFactoryConfiguration.class, diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfiguration.java index 7233e17..8cf30b3 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfiguration.java @@ -26,6 +26,7 @@ import org.springframework.grpc.client.GlobalClientInterceptor; @AutoConfiguration( afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration") +@ConditionalOnGrpcClientEnabled @ConditionalOnClass({ ObservationRegistry.class, ObservationGrpcClientInterceptor.class }) @ConditionalOnBean(ObservationRegistry.class) @ConditionalOnProperty(name = "spring.grpc.client.observation.enabled", havingValue = "true", matchIfMissing = true) diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index ea65bda..6ad3d98 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -35,6 +35,12 @@ "description": "Whether to enable Observations on the server.", "defaultValue": true }, + { + "name": "spring.grpc.client.enabled", + "type": "java.lang.Boolean", + "description": "Whether to enable client autoconfiguration.", + "defaultValue": true + }, { "name": "spring.grpc.client.observations.enabled", "type": "java.lang.Boolean", diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfigurationTests.java index 423d3c8..7afe8c6 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientAutoConfigurationTests.java @@ -51,6 +51,7 @@ import io.grpc.CompressorRegistry; import io.grpc.DecompressorRegistry; import io.grpc.ManagedChannelBuilder; import io.grpc.netty.NettyChannelBuilder; +import io.grpc.stub.AbstractStub; /** * Tests for {@link GrpcClientAutoConfiguration}. @@ -65,6 +66,32 @@ class GrpcClientAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(GrpcClientAutoConfiguration.class, SslAutoConfiguration.class)); } + @Test + void whenGrpcStubNotOnClasspathThenAutoConfigurationIsSkipped() { + this.contextRunner() + .withClassLoader(new FilteredClassLoader(AbstractStub.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcClientAutoConfiguration.class)); + } + + @Test + void whenClientEnabledPropertySetFalseThenAutoConfigurationIsSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.client.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(GrpcClientAutoConfiguration.class)); + } + + @Test + void whenClientEnabledPropertyNotSetThenAutoConfigurationIsNotSkipped() { + this.contextRunner().run((context) -> assertThat(context).hasSingleBean(GrpcClientAutoConfiguration.class)); + } + + @Test + void whenClientEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.client.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcClientAutoConfiguration.class)); + } + @Test void whenHasUserDefinedCredentialsProviderDoesNotAutoConfigureBean() { ChannelCredentialsProvider customCredentialsProvider = mock(ChannelCredentialsProvider.class); diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfigurationTests.java index fa2acec..aaf8510 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/client/GrpcClientObservationAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.grpc.autoconfigure.client; +import io.grpc.stub.AbstractStub; import io.micrometer.core.instrument.binder.grpc.ObservationGrpcClientInterceptor; import io.micrometer.observation.ObservationRegistry; import org.junit.jupiter.api.Test; @@ -75,6 +76,33 @@ class GrpcClientObservationAutoConfigurationTests { .run(context -> assertThat(context).doesNotHaveBean(GrpcClientObservationAutoConfiguration.class)); } + @Test + void whenClientEnabledPropertyNotSetThenAutoConfigNotSkipped() { + this.validContextRunner() + .run(context -> assertThat(context).hasSingleBean(GrpcClientObservationAutoConfiguration.class)); + } + + @Test + void whenClientEnabledPropertySetTrueThenAutoConfigIsNotSkipped() { + this.validContextRunner() + .withPropertyValues("spring.grpc.client.enabled=true") + .run(context -> assertThat(context).hasSingleBean(GrpcClientObservationAutoConfiguration.class)); + } + + @Test + void whenClientEnabledPropertySetFalseThenAutoConfigIsSkipped() { + this.validContextRunner() + .withPropertyValues("spring.grpc.client.enabled=false") + .run(context -> assertThat(context).doesNotHaveBean(GrpcClientObservationAutoConfiguration.class)); + } + + @Test + void whenGrpcStubNotOnClasspathThenAutoConfigIsSkipped() { + this.validContextRunner() + .withClassLoader(new FilteredClassLoader(AbstractStub.class)) + .run(context -> assertThat(context).doesNotHaveBean(GrpcClientObservationAutoConfiguration.class)); + } + @Test void whenAllConditionsAreMetThenInterceptorConfiguredAsExpected() { this.validContextRunner().run((context) -> { @@ -86,4 +114,4 @@ class GrpcClientObservationAutoConfigurationTests { }); } -} \ No newline at end of file +}