diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurations.java index 85d90bd945c..72c196ceb55 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinConfigurations.java @@ -16,8 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.tracing.zipkin; -import java.util.List; - import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; import zipkin2.Span; import zipkin2.codec.BytesEncoder; @@ -27,6 +25,7 @@ import zipkin2.reporter.Sender; import zipkin2.reporter.brave.ZipkinSpanHandler; import zipkin2.reporter.urlconnection.URLConnectionSender; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -76,10 +75,10 @@ class ZipkinConfigurations { @Bean @ConditionalOnMissingBean(Sender.class) ZipkinRestTemplateSender restTemplateSender(ZipkinProperties properties, - List customizers) { + ObjectProvider customizers) { RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder() .setConnectTimeout(properties.getConnectTimeout()).setReadTimeout(properties.getReadTimeout()); - customizers.forEach((c) -> c.customize(restTemplateBuilder)); + customizers.orderedStream().forEach((c) -> c.customize(restTemplateBuilder)); return new ZipkinRestTemplateSender(properties.getEndpoint(), restTemplateBuilder.build()); } @@ -93,9 +92,9 @@ class ZipkinConfigurations { @Bean @ConditionalOnMissingBean(Sender.class) ZipkinWebClientSender webClientSender(ZipkinProperties properties, - List customizers) { + ObjectProvider customizers) { WebClient.Builder builder = WebClient.builder(); - customizers.forEach((c) -> c.customize(builder)); + customizers.orderedStream().forEach((c) -> c.customize(builder)); return new ZipkinWebClientSender(properties.getEndpoint(), builder.build()); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateBuilderCustomizer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateBuilderCustomizer.java new file mode 100644 index 00000000000..a1ee937d3e7 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinRestTemplateBuilderCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2022 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.boot.actuate.autoconfigure.tracing.zipkin; + +import org.springframework.boot.web.client.RestTemplateBuilder; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link RestTemplateBuilder} used to send spans to Zipkin. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@FunctionalInterface +public interface ZipkinRestTemplateBuilderCustomizer { + + /** + * Customize the rest template builder. + * @param restTemplateBuilder the {@code RestTemplateBuilder} to customize + */ + void customize(RestTemplateBuilder restTemplateBuilder); + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientBuilderCustomizer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientBuilderCustomizer.java new file mode 100644 index 00000000000..bd6be6e9332 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinWebClientBuilderCustomizer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2022 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.boot.actuate.autoconfigure.tracing.zipkin; + +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClient.Builder; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link Builder} used to send spans to Zipkin. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@FunctionalInterface +public interface ZipkinWebClientBuilderCustomizer { + + /** + * Customize the web client builder. + * @param webClientBuilder the {@code WebClient.Builder} to customize + */ + void customize(WebClient.Builder webClientBuilder); + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationIntegrationTests.java new file mode 100644 index 00000000000..c6aeaf336a5 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/zipkin/ZipkinAutoConfigurationIntegrationTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012-2022 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.boot.actuate.autoconfigure.tracing.zipkin; + +import org.junit.jupiter.api.Test; +import zipkin2.reporter.urlconnection.URLConnectionSender; + +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.assertj.ApplicationContextAssertProvider; +import org.springframework.boot.test.context.runner.AbstractApplicationContextRunner; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.context.ConfigurableApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link ZipkinAutoConfiguration} and other related + * auto-configurations. + * + * @author Andy Wilkinson + */ +class ZipkinAutoConfigurationIntegrationTests { + + @Test + void zipkinsUseOfRestTemplateDoesNotCauseACycle() { + configure(new WebApplicationContextRunner()) + .withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class)) + .run((context) -> assertThat(context).hasNotFailed()); + } + + @Test + void zipkinsUseOfWebClientDoesNotCauseACycle() { + configure(new ReactiveWebApplicationContextRunner()) + .withConfiguration(AutoConfigurations.of(WebClientAutoConfiguration.class)) + .run((context) -> assertThat(context).hasNotFailed()); + } + + , C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider> AbstractApplicationContextRunner configure( + AbstractApplicationContextRunner runner) { + return runner + .withConfiguration(AutoConfigurations.of(MicrometerTracingAutoConfiguration.class, + ObservationAutoConfiguration.class, BraveAutoConfiguration.class, ZipkinAutoConfiguration.class, + HttpClientMetricsAutoConfiguration.class, MetricsAutoConfiguration.class, + SimpleMetricsExportAutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(URLConnectionSender.class)); + } + +}