diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index fd8ac3c84..5fa16c009 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -17,12 +17,13 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.Map; import javax.annotation.PostConstruct; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -36,9 +37,9 @@ import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpSpanInjector; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.security.oauth2.client.OAuth2RestTemplate; @@ -71,14 +72,14 @@ public class TraceWebClientAutoConfiguration { @Configuration protected static class TraceInterceptorConfiguration { - @Autowired(required = false) - private Collection restTemplates; + @Autowired + private ApplicationContext applicationContext; @Autowired private TraceRestTemplateInterceptor traceRestTemplateInterceptor; @Bean - @Order(Ordered.HIGHEST_PRECEDENCE) + @Order RestTemplateCustomizer traceRestTemplateCustomizer() { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { @@ -90,11 +91,12 @@ public class TraceWebClientAutoConfiguration { @PostConstruct public void init() { - if (this.restTemplates != null) { - for (RestTemplate restTemplate : this.restTemplates) { - new RestTemplateInterceptorInjector( - this.traceRestTemplateInterceptor).inject(restTemplate); - } + Map restTemplates = BeanFactoryUtils + .beansOfTypeIncludingAncestors(this.applicationContext, + RestTemplate.class); + for (RestTemplate restTemplate : restTemplates.values()) { + new RestTemplateInterceptorInjector( + this.traceRestTemplateInterceptor).inject(restTemplate); } } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java index 40e6702e1..448acbfec 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java @@ -1,22 +1,40 @@ +/* + * Copyright 2013-2018 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 + * + * http://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.cloud.sleuth.instrument.web.client; -import org.assertj.core.api.BDDAssertions; +import java.io.IOException; +import java.util.List; + import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateCustomizer; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; -import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; -import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration; -import org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.BDDAssertions.then; @@ -27,36 +45,84 @@ import static org.assertj.core.api.BDDAssertions.then; @SpringBootTest(classes = TraceWebClientAutoConfigurationTests.Config.class) public class TraceWebClientAutoConfigurationTests { - @Autowired Config config; - @Autowired UserInfoRestTemplateCustomizer customizer; - @Autowired TraceRestTemplateInterceptor interceptor; + @Autowired @Qualifier("firstRestTemplate") RestTemplate restTemplate; + @Autowired @Qualifier("secondRestTemplate") RestTemplate secondRestTemplate; @Test - public void should_wrap_UserInfoRestTemplateCustomizer_in_a_trace_representation() { - OAuth2ProtectedResourceDetails details = Mockito.mock(OAuth2ProtectedResourceDetails.class); - OAuth2RestTemplate template = new OAuth2RestTemplate(details); - - this.customizer.customize(template); - - then(this.config.executed).isTrue(); - then(template.getInterceptors()).contains(this.interceptor); + public void should_add_rest_template_interceptors() { + assertInterceptorsOrder(assertInterceptorsNotEmpty(this.restTemplate)); + assertInterceptorsOrder(assertInterceptorsNotEmpty(this.secondRestTemplate)); } + private List assertInterceptorsNotEmpty(RestTemplate restTemplate) { + then(restTemplate).isNotNull(); + List interceptors = restTemplate + .getInterceptors(); + then(interceptors).isNotEmpty(); + return interceptors; + } + + private void assertInterceptorsOrder( + List interceptors) { + int traceInterceptorIndex = 0; + int myInterceptorIndex = 0; + int mySecondInterceptorIndex = 0; + for (int i = 0; i < interceptors.size(); i++) { + if (interceptors.get(i) instanceof TraceRestTemplateInterceptor) { + traceInterceptorIndex = i; + } else if (interceptors.get(i) instanceof MyClientHttpRequestInterceptor) { + myInterceptorIndex = i; + } else if (interceptors.get(i) instanceof MySecondClientHttpRequestInterceptor) { + mySecondInterceptorIndex = i; + } + } + then(traceInterceptorIndex) + .isLessThan(myInterceptorIndex) + .isLessThan(mySecondInterceptorIndex); + } @Configuration - @ImportAutoConfiguration(classes = { - TraceWebClientAutoConfiguration.class, SleuthLogAutoConfiguration.class, - TraceHttpAutoConfiguration.class, TraceWebAutoConfiguration.class, TraceAutoConfiguration.class }) + @EnableAutoConfiguration static class Config { - boolean executed = false; + @Bean + @Qualifier("firstRestTemplate") + RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder + .additionalInterceptors(new MyClientHttpRequestInterceptor()) + .build(); + } - @Bean UserInfoRestTemplateCustomizer customizer() { - return new UserInfoRestTemplateCustomizer() { - @Override public void customize(OAuth2RestTemplate template) { - Config.this.executed = true; - } + @Bean + @Qualifier("secondRestTemplate") + RestTemplate secondRestTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder + .additionalInterceptors(new MyClientHttpRequestInterceptor()) + .build(); + } + + @Bean + RestTemplateCustomizer myRestTemplateCustomizer() { + return restTemplate -> { + restTemplate.getInterceptors().add(0, new MySecondClientHttpRequestInterceptor()); }; } + + } +} + +class MyClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + + @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, + ClientHttpRequestExecution execution) throws IOException { + return execution.execute(request, body); + } +} + +class MySecondClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + + @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, + ClientHttpRequestExecution execution) throws IOException { + return execution.execute(request, body); } } \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientUserInfoCustomizerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientUserInfoCustomizerTests.java new file mode 100644 index 000000000..46ff55e2d --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientUserInfoCustomizerTests.java @@ -0,0 +1,63 @@ +package org.springframework.cloud.sleuth.instrument.web.client; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateCustomizer; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration; +import org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * @author Marcin Grzejszczak + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = TraceWebClientUserInfoCustomizerTests.Config.class) +public class TraceWebClientUserInfoCustomizerTests { + + @Autowired Config config; + @Autowired UserInfoRestTemplateCustomizer customizer; + @Autowired TraceRestTemplateInterceptor interceptor; + + @Test + public void should_wrap_UserInfoRestTemplateCustomizer_in_a_trace_representation() { + OAuth2ProtectedResourceDetails details = Mockito.mock(OAuth2ProtectedResourceDetails.class); + OAuth2RestTemplate template = new OAuth2RestTemplate(details); + + this.customizer.customize(template); + + then(this.config.executed).isTrue(); + then(template.getInterceptors()).contains(this.interceptor); + } + + + @Configuration + @ImportAutoConfiguration(classes = { + TraceWebClientAutoConfiguration.class, SleuthLogAutoConfiguration.class, + TraceHttpAutoConfiguration.class, TraceWebAutoConfiguration.class, TraceAutoConfiguration.class }) + static class Config { + + boolean executed = false; + + @Bean UserInfoRestTemplateCustomizer customizer() { + return new UserInfoRestTemplateCustomizer() { + @Override public void customize(OAuth2RestTemplate template) { + Config.this.executed = true; + } + }; + } + } +} \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java index ef3a6fc43..ca3d82ae4 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java @@ -368,8 +368,6 @@ public class WebClientTests { @Override public void customize(RestTemplate restTemplate) { this.executed = true; - then(restTemplate.getInterceptors().get(0)).isInstanceOf( - TraceRestTemplateInterceptor.class); } public boolean isExecuted() {