diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc index f06dee89a7a4..592ef98102cd 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/dev-services.adoc @@ -152,6 +152,7 @@ SSL is supported for the following service connections: * Cassandra * Elasticsearch * MongoDB +* OpenTelemetry (OTLP logging, metrics, and tracing) * RabbitMQ * RabbitMQ Streams * Redis diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/external-config.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/external-config.adoc index 9ce3d49f2430..1f3ab5cec2d8 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/external-config.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/features/external-config.adoc @@ -955,7 +955,7 @@ Default Values defined in configuration properties are not reflected in the java In the examples above, the `enabled` property of `MyProperties` bound to `my.service` is `false` by default. However, `my.service.enabled` is not available in the `Environment` with a value of `false` if no such property is set by the user. -Concretely, this prevents you to use `@Value(${"my.service.enabled"})` or `my.service.enabled` as a placeholder in configuration properties without explicitly providing a default. +Concretely, this prevents you from using `@Value("${my.service.enabled}")` or `my.service.enabled` as a placeholder in configuration properties without explicitly providing a default. If you need to query the javadoc:org.springframework.core.env.Environment[] for that property, for instance in a javadoc:org.springframework.context.annotation.Condition[] implementation, the default needs to be provided as well. diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/grpc.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/grpc.adoc index 6cc9cc9f0f44..466933a3a9ac 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/grpc.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/grpc.adoc @@ -2,16 +2,16 @@ = gRPC Google Remote Procedure Call (gRPC) is a high-performance RPC framework that enables client-server communication using binary messages. -Spring Boot include support for developing and testing both client and server gRPC applications. +Spring Boot includes support for developing and testing both client and server gRPC applications. -The underling message format used by gRPC is Protocol Buffers which allow messages to be created and consumed by a wide variety of programming languages. +The underlying message format used by gRPC is Protocol Buffers, which allow messages to be created and consumed by a wide variety of programming languages. [[io.grpc.servicedefinitions]] == Service Definitions -To develop a gRPC application you first need a Protocol Buffers service definition file. +To develop a gRPC application, you first need a Protocol Buffers service definition file. A `.proto` file defines the services and messages that your application can consume or provide. Here's an example of a typical `.proto` file that uses https://protobuf.dev/programming-guides/proto3/[the `proto3` revision] of the protocol buffers language: @@ -36,11 +36,11 @@ message HelloReply { } ---- -This file defines a `HelloWorld` service with a single method that accepts a `HelloReqest` message and return a `HelloReply` message. -The `HelloReqest` message contains a `name` string field. +This file defines a `HelloWorld` service with a single method that accepts a `HelloRequest` message and returns a `HelloReply` message. +The `HelloRequest` message contains a `name` string field. The `HelloReply` message contains a `message` string field. -With the exception of a the `java_package` and `java_multiple_files` options, there is nothing in the `.proto` file that is specific to the Java programming langage. +With the exception of the `java_package` and `java_multiple_files` options, there is nothing in the `.proto` file that is specific to the Java programming language. @@ -48,17 +48,17 @@ With the exception of a the `java_package` and `java_multiple_files` options, th === Generating Java Code Since `.proto` files are language agnostic, we need a process to convert them into usable Java code. -We can then use the generated code to either make a remote procedure call to running service, or implement the service ourselves so that others may call it. +We can then use the generated code to either make a remote procedure call to a running service or implement the service ourselves so that others may call it. The exact process you use to generate code will depend on your build system. -Spring Boot supports for both Maven and Gradle protobuf plugins, but you are free to use whatever solution works best for you. +Spring Boot supports both Maven and Gradle protobuf plugins, but you are free to use whatever solution works best for you. [[io.grpc.servicedefinitions.generatingjavacode.maven]] ==== Using the Maven Plugin -Spring Boot include dependency management for the `io.github.ascopes:protobuf-maven-plugin` Maven plugin. +Spring Boot includes dependency management for the `io.github.ascopes:protobuf-maven-plugin` Maven plugin. If you are using the `spring-boot-starter-parent` POM, you'll also get sensible out-of-the-box configuration. The following shows a typical Maven POM file that uses the plugin: @@ -131,7 +131,7 @@ If you use Spring Boot's dependency management the `${protobuf-java.version}` an [[io.grpc.servicedefinitions.generatingjavacode.gradle]] ==== Using the Gradle Plugin -Spring Boot include dependency management for the `com.google.protobuf:protobuf-gradle-plugin` Gradle plugin. +Spring Boot includes dependency management for the `com.google.protobuf:protobuf-gradle-plugin` Gradle plugin. In addition, the `spring-boot-gradle-plugin` will react to the presence of the protobuf plugin and configure it appropriately. The following shows a typical Gradle file that uses the plugin: @@ -163,7 +163,7 @@ endif::[] } ---- -Since this gradle file uses both the `org.springframework.boot` and `com.google.protobuf` plugins, you'll get the following: +Since this Gradle file uses both the `org.springframework.boot` and `com.google.protobuf` plugins, you'll get the following: * Configuration of the `protoc` version. * Configuration of the `protoc-gen-grpc-java` version. @@ -179,11 +179,11 @@ TIP: If you don't use `org.springframework.boot` plugin, or you want to configur Spring Boot provides a `spring-boot-grpc-server` module and a `spring-boot-starter-grpc-server` starter POM that you can use for server applications. -In order to write the actual server code, you'll need to extended one or more of base classes generated from your `.proto` file and expose them as Spring beans. +In order to write the actual server code, you'll need to extend one or more of the base classes generated from your `.proto` file and expose them as Spring beans. Spring gRPC will automatically expose any bean that implements javadoc:io.grpc.BindableService[] as a gRPC server. Since all `.proto` generated classes implement javadoc:io.grpc.BindableService[], adding them as beans is enough to expose them over gRPC. -TIP: For more details see {url-spring-grpc-docs}/server.html#_create_a_grpc_service[the Spring gRPC documentation]. +TIP: For more details, see {url-spring-grpc-docs}/server.html#_create_a_grpc_service[the Spring gRPC documentation]. The following example shows how the `HelloWorld` service from the `.proto` file above could be implemented. In this example, we're using the javadoc:org.springframework.grpc.server.service.GrpcService[format=annotation] annotation and assuming that the code is in a package that will be picked up by component scanning: @@ -213,7 +213,7 @@ $ grpcurl -d '{"name":"Spring"}' -plaintext localhost:9090 HelloWorld.SayHello If you find that the version of Netty provided by the `spring-boot-starter-grpc-server` starter POM isn't compatible with other libraries you use, you can switch to a "`shaded`" version. -To switch, you can excluded `io.grpc:grpc-netty` and include `io.grpc:grpc-netty-shaded`. +To switch, you can exclude `io.grpc:grpc-netty` and include `io.grpc:grpc-netty-shaded`. For example: @@ -317,10 +317,10 @@ dependencies { + ====== -TIP: Remember to include a Servlet Container dependency, for example using `spring-boot-starter-tomcat`, and to set `server.http2.enabled` to `true`. +TIP: Remember to include a Servlet Container dependency – `spring-boot-starter-tomcat`, for example – and to set configprop:server.http2.enabled[] to `true`. NOTE: When using a servlet container, certain gRPC server configuration properties are not relevant and will be ignored. -For example, configprop:spring.grpc.server.port[] is ignored since configprop:server.port[] used used to set a web server port. +For example, configprop:spring.grpc.server.port[] is ignored since configprop:server.port[] is used to set a web server port. @@ -343,7 +343,7 @@ spring: Client authentication can also be configured by setting configprop:spring.grpc.server.ssl.client-auth[] to `optional` or `require`. -TIP: To temporarily disable server SSL support, for example to aid with testing, you can set configprop:spring.grpc.server.ssl.enabled[] to `false`. +TIP: To temporarily disable server SSL support – to aid with testing, for example – set configprop:spring.grpc.server.ssl.enabled[] to `false`. @@ -604,7 +604,7 @@ You can configure gRPC connections to use standard one-way-TLS, or mutual TLS ==== Standard one-way TLS To use standard one-way TLS, you can set the `ssl.enabled` property to `true` in your channel properties. -For example, the following will enabled an SSL/TLS connection for the `myservice` channel: +For example, the following will enable an SSL/TLS connection for the `myservice` channel: [configprops,yaml] ---- @@ -643,7 +643,7 @@ spring: [TIP] ==== -To temporarily disable client SSL support, for example to aid with testing, you can set `bypass-certificate-validation` to `true` on your channel config: +To temporarily disable client SSL support – to aid with testing, for example – set `bypass-certificate-validation` to `true` on your channel config: [configprops,yaml] ---- @@ -667,7 +667,7 @@ In this mode, the in-process channel factory is auto-configured in addition to t To prevent users from having to deal with multiple channel factories, a composite channel factory is configured as the primary channel factory bean. The composite consults its composed factories to find the first one that supports the channel target. -To use the in-process server the channel target must be set to `in-process:` +To use the in-process server, the channel target must be set to `in-process:` TIP: To disable the in-process channel factory, you can set the configprop:spring.grpc.client.inprocess.enabled[] property to `false`. @@ -700,14 +700,14 @@ include-code::MyGrpcConfiguration[] [[io.grpc.testing]] == Testing gRPC Applications -To help test your gRPC client and server applications you can use the `spring-boot-grpc-test` module or the `spring-boot-starter-grpc-client-test` / `spring-boot-starter-grpc-server-test` starter POMs. +To help test your gRPC client and server applications, you can use the `spring-boot-grpc-test` module or the `spring-boot-starter-grpc-client-test` / `spring-boot-starter-grpc-server-test` starter POMs. [[io.grpc.testing.test-transport]] === Using In-Process Test Transport The javadoc:org.springframework.boot.grpc.test.autoconfigure.AutoConfigureTestGrpcTransport[format=annotation] annotation allows you to quickly replace gRPC communication channels with in-process channels specifically designed for testing. -Unlike regular in-process channels, these test channels to not require any configuration. +Unlike regular in-process channels, these test channels do not require any configuration. Using test gRPC transport means that you don't need to actually listen on a network port to start your application. This allows your tests to run quickly, whilst still ensuring that your application works as expected. @@ -740,4 +740,4 @@ include-code::MyGrpcIntegrationTests[] -// FIXME https://github.com/spring-projects/spring-grpc/issues/397 \ No newline at end of file +// FIXME https://github.com/spring-projects/spring-grpc/issues/397 diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc index 92bde635fa73..4142d8eead44 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc @@ -241,6 +241,7 @@ The SSL annotations are supported for the following service connections: * Elasticsearch * Kafka * MongoDB +* OpenTelemetry (OTLP logging, metrics, and tracing) * RabbitMQ * RabbitMQ Streams * Redis diff --git a/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java b/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java index fbf6b2723399..8a0f5dacd0f1 100644 --- a/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java +++ b/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java @@ -87,15 +87,6 @@ public final class CloudFoundryActuatorAutoConfiguration { private static final String BASE_PATH = "/cloudfoundryapplication"; - @Bean - @ConditionalOnMissingBean - @ConditionalOnAvailableEndpoint - @ConditionalOnBean({ HealthEndpoint.class, HealthEndpointWebExtension.class }) - CloudFoundryHealthEndpointWebExtension cloudFoundryHealthEndpointWebExtension( - HealthEndpointWebExtension healthEndpointWebExtension) { - return new CloudFoundryHealthEndpointWebExtension(healthEndpointWebExtension); - } - @Bean @ConditionalOnMissingBean @ConditionalOnAvailableEndpoint @@ -157,6 +148,21 @@ private static CorsConfiguration getCorsConfiguration() { return corsConfiguration; } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(HealthEndpoint.class) + static class HealthConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + @ConditionalOnBean({ HealthEndpoint.class, HealthEndpointWebExtension.class }) + CloudFoundryHealthEndpointWebExtension cloudFoundryHealthEndpointWebExtension( + HealthEndpointWebExtension healthEndpointWebExtension) { + return new CloudFoundryHealthEndpointWebExtension(healthEndpointWebExtension); + } + + } + /** * {@link WebSecurityConfigurer} to tell Spring Security to permit cloudfoundry * specific paths. The Cloud foundry endpoints are protected by their own security diff --git a/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java b/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java index 149aadbf180a..c66bee13972a 100644 --- a/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java +++ b/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java @@ -47,6 +47,7 @@ import org.springframework.boot.servlet.autoconfigure.actuate.web.ServletManagementContextAutoConfiguration; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration; import org.springframework.context.ApplicationContext; @@ -92,6 +93,15 @@ class CloudFoundryActuatorAutoConfigurationTests { ServletManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class)); + @Test + @ClassPathExclusions(packages = "org.springframework.boot.health.actuate.endpoint") + void refreshSucceedsWithoutHealth() { + this.contextRunner + .withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id", + "vcap.application.cf_api:https://my-cloud-controller.com") + .run((context) -> assertThat(context).hasNotFailed()); + } + @Test void cloudFoundryPlatformActive() { this.contextRunner diff --git a/module/spring-boot-jersey/src/main/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfiguration.java b/module/spring-boot-jersey/src/main/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfiguration.java index fa8977b4c5ea..ed288b648a4f 100644 --- a/module/spring-boot-jersey/src/main/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfiguration.java +++ b/module/spring-boot-jersey/src/main/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfiguration.java @@ -58,6 +58,7 @@ import org.springframework.boot.jersey.actuate.endpoint.web.JerseyHealthEndpointAdditionalPathResourceFactory; import org.springframework.boot.jersey.autoconfigure.ResourceConfigCustomizer; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; @@ -92,21 +93,6 @@ JerseyWebEndpointsResourcesRegistrar jerseyWebEndpointsResourcesRegistrar(Enviro endpointMediaTypes, basePath, shouldRegisterLinks); } - @Bean - @ConditionalOnManagementPort(ManagementPortType.DIFFERENT) - @ConditionalOnBean(HealthEndpoint.class) - @ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB) - JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar jerseyDifferentPortAdditionalHealthEndpointPathsResourcesRegistrar( - WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups healthEndpointGroups) { - Collection webEndpoints = webEndpointsSupplier.getEndpoints(); - ExposableWebEndpoint healthEndpoint = webEndpoints.stream() - .filter((endpoint) -> endpoint.getEndpointId().equals(HEALTH_ENDPOINT_ID)) - .findFirst() - .orElse(null); - return new JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(healthEndpoint, - healthEndpointGroups); - } - @Bean @ConditionalOnBean(org.springframework.boot.actuate.endpoint.jackson.EndpointJackson2ObjectMapper.class) @SuppressWarnings("removal") @@ -122,6 +108,27 @@ private boolean shouldRegisterLinksMapping(WebEndpointProperties properties, Env || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT)); } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(HealthEndpoint.class) + static class HealthConfiguration { + + @Bean + @ConditionalOnManagementPort(ManagementPortType.DIFFERENT) + @ConditionalOnBean(HealthEndpoint.class) + @ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB) + JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar jerseyDifferentPortAdditionalHealthEndpointPathsResourcesRegistrar( + WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups healthEndpointGroups) { + Collection webEndpoints = webEndpointsSupplier.getEndpoints(); + ExposableWebEndpoint healthEndpoint = webEndpoints.stream() + .filter((endpoint) -> endpoint.getEndpointId().equals(HEALTH_ENDPOINT_ID)) + .findFirst() + .orElse(null); + return new JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(healthEndpoint, + healthEndpointGroups); + } + + } + /** * Register endpoints with the {@link ResourceConfig} for the management context. */ @@ -178,7 +185,7 @@ private void register(Collection resources, ResourceConfig config) { } - class JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar + static class JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar implements ManagementContextResourceConfigCustomizer { private final @Nullable ExposableWebEndpoint healthEndpoint; diff --git a/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfigurationTests.java b/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfigurationTests.java index 972dfadb32f3..4bd237b545dc 100644 --- a/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfigurationTests.java +++ b/module/spring-boot-jersey/src/test/java/org/springframework/boot/jersey/autoconfigure/actuate/web/JerseyWebEndpointManagementContextConfigurationTests.java @@ -30,6 +30,7 @@ import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import static org.assertj.core.api.Assertions.assertThat; @@ -52,6 +53,12 @@ void jerseyWebEndpointsResourcesRegistrarForEndpointsIsAutoConfigured() { this.runner.run((context) -> assertThat(context).hasSingleBean(JerseyWebEndpointsResourcesRegistrar.class)); } + @Test + @ClassPathExclusions(packages = "org.springframework.boot.health.actuate.endpoint") + void refreshSucceedsWithoutHealth() { + this.runner.run((context) -> assertThat(context).hasNotFailed()); + } + @Test void autoConfigurationIsConditionalOnServletWebApplication() { ApplicationContextRunner contextRunner = new ApplicationContextRunner() diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OpenTelemetryResourceAttributes.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OpenTelemetryResourceAttributes.java index 65d28dee9050..f051c574b401 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OpenTelemetryResourceAttributes.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OpenTelemetryResourceAttributes.java @@ -86,10 +86,11 @@ public OpenTelemetryResourceAttributes(Environment environment, @Nullable Map - * Additionally, {@code spring.application.name} or {@code unknown_service} will be - * used as the default for {@code service.name}, and {@code spring.application.group} - * will serve as the default for {@code service.group} and {@code service.namespace}. - * @param consumer the {@link BiConsumer} to apply + * Additionally, {@code spring.application.name}, or {@code unknown_service} if it is + * not set, will be used as the default value for {@code service.name}, and + * {@code spring.application.group} will serve as the default value for + * {@code service.namespace}. + * @param consumer the consumer to apply resource attributes to */ public void applyTo(BiConsumer consumer) { Assert.notNull(consumer, "'consumer' must not be null");