Ensures that all Lazy components are resolving any beans when context is running
fixes gh-1750
This commit is contained in:
@@ -52,6 +52,7 @@ import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.LocalServiceName;
|
||||
import org.springframework.cloud.sleuth.SpanAdjuster;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.SleuthContextListener;
|
||||
import org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.sampler.SamplerAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -104,6 +105,11 @@ public class TraceAutoConfiguration {
|
||||
return 0;
|
||||
};
|
||||
|
||||
@Bean
|
||||
SleuthContextListener traceContextClosedListener() {
|
||||
return new SleuthContextListener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
// NOTE: stable bean name as might be used outside sleuth
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.sleuth.instrument.async;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.instrument.scheduling.SleuthSchedulingProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
@@ -38,9 +37,4 @@ import org.springframework.context.annotation.Configuration;
|
||||
SleuthSchedulingProperties.class })
|
||||
public class AsyncAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
SleuthContextListener traceContextClosedListener() {
|
||||
return new SleuthContextListener();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
|
||||
/**
|
||||
* {@link Executor} that wraps {@link Runnable} in a trace representation.
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
import org.springframework.core.task.TaskDecorator;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
@@ -30,6 +30,7 @@ import brave.Tracing;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
|
||||
/**
|
||||
* A decorator class for {@link ExecutorService} to support tracing in Executors.
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
|
||||
/**
|
||||
* A decorator class for {@link ScheduledExecutorService} to support tracing in Executors.
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.boot.web.client.RestTemplateCustomizer;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.internal.ContextUtil;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -309,10 +310,17 @@ class LazyTracingClientHttpRequestInterceptor implements ClientHttpRequestInterc
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
if (isContextUnusable()) {
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
return interceptor().intercept(request, body, execution);
|
||||
}
|
||||
|
||||
private TracingClientHttpRequestInterceptor interceptor() {
|
||||
boolean isContextUnusable() {
|
||||
return ContextUtil.isContextUnusable(this.beanFactory);
|
||||
}
|
||||
|
||||
ClientHttpRequestInterceptor interceptor() {
|
||||
if (this.interceptor == null) {
|
||||
this.interceptor = this.beanFactory
|
||||
.getBean(TracingClientHttpRequestInterceptor.class);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async;
|
||||
package org.springframework.cloud.sleuth.internal;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -22,12 +22,12 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
/**
|
||||
* Utility class that verifies that context is in creation.
|
||||
* Utility class that verifies that context is in creation. Do not use.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.1.0
|
||||
*/
|
||||
final class ContextUtil {
|
||||
public final class ContextUtil {
|
||||
|
||||
private ContextUtil() {
|
||||
throw new IllegalStateException("Can't instantiate a utility class");
|
||||
@@ -35,7 +35,11 @@ final class ContextUtil {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ContextUtil.class);
|
||||
|
||||
static boolean isContextUnusable(BeanFactory beanFactory) {
|
||||
/**
|
||||
* @param beanFactory bean facotry
|
||||
* @return {@code true} when context is not ready to be used
|
||||
*/
|
||||
public static boolean isContextUnusable(BeanFactory beanFactory) {
|
||||
SleuthContextListener listener = SleuthContextListener.getBean(beanFactory);
|
||||
boolean contextUnusable = listener.isUnusable();
|
||||
if (contextUnusable && log.isDebugEnabled()) {
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async;
|
||||
package org.springframework.cloud.sleuth.internal;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -32,7 +32,13 @@ import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
|
||||
class SleuthContextListener implements SmartApplicationListener {
|
||||
/**
|
||||
* Internal tool used by Sleuth. Do not use.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.2.5
|
||||
*/
|
||||
public class SleuthContextListener implements SmartApplicationListener {
|
||||
|
||||
static final Map<BeanFactory, SleuthContextListener> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -42,7 +48,7 @@ class SleuthContextListener implements SmartApplicationListener {
|
||||
|
||||
final AtomicBoolean closed;
|
||||
|
||||
SleuthContextListener() {
|
||||
public SleuthContextListener() {
|
||||
this.refreshed = new AtomicBoolean();
|
||||
this.closed = new AtomicBoolean();
|
||||
}
|
||||
@@ -52,7 +58,13 @@ class SleuthContextListener implements SmartApplicationListener {
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
static SleuthContextListener getBean(BeanFactory beanFactory) {
|
||||
/**
|
||||
* Returns an instance of the {@link SleuthContextListener} that might have already
|
||||
* been initialized.
|
||||
* @param beanFactory bean factory
|
||||
* @return instance of {@link SleuthContextListener}
|
||||
*/
|
||||
public static SleuthContextListener getBean(BeanFactory beanFactory) {
|
||||
return CACHE.getOrDefault(beanFactory, new SleuthContextListener());
|
||||
}
|
||||
|
||||
@@ -83,7 +95,10 @@ class SleuthContextListener implements SmartApplicationListener {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isUnusable() {
|
||||
/**
|
||||
* @return @{code true} when Spring Context has NOT yet been started
|
||||
*/
|
||||
public boolean isUnusable() {
|
||||
return !this.refreshed.get() || this.closed.get();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.SpanNamer;
|
||||
import org.springframework.cloud.sleuth.internal.SleuthContextListenerAccessor;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import brave.spring.web.TracingClientHttpRequestInterceptor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.BDDMockito;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
class LazyTracingClientHttpRequestInterceptorTests {
|
||||
|
||||
@Test
|
||||
void should_not_retrieve_bean_from_context_when_context_is_not_ready()
|
||||
throws IOException {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
LazyTracingClientHttpRequestInterceptor interceptor = new LazyTracingClientHttpRequestInterceptor(
|
||||
beanFactory);
|
||||
|
||||
interceptor.intercept(mock(HttpRequest.class), new byte[0],
|
||||
mock(ClientHttpRequestExecution.class));
|
||||
|
||||
then(beanFactory).should(never())
|
||||
.getBean(TracingClientHttpRequestInterceptor.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_retrieve_bean_from_context_when_context_is_ready() throws IOException {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
ClientHttpRequestInterceptor requestInterceptor = mock(
|
||||
ClientHttpRequestInterceptor.class);
|
||||
LazyTracingClientHttpRequestInterceptor interceptor = new LazyTracingClientHttpRequestInterceptor(
|
||||
beanFactory) {
|
||||
@Override
|
||||
ClientHttpRequestInterceptor interceptor() {
|
||||
return requestInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isContextUnusable() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
interceptor.intercept(mock(HttpRequest.class), new byte[0],
|
||||
mock(ClientHttpRequestExecution.class));
|
||||
|
||||
then(requestInterceptor).should().intercept(BDDMockito.any(HttpRequest.class),
|
||||
BDDMockito.any(byte[].class),
|
||||
BDDMockito.any(ClientHttpRequestExecution.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async;
|
||||
package org.springframework.cloud.sleuth.internal;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
Reference in New Issue
Block a user