Added a fix for special cases of executors that fail to be wrapped in a cglib proxy

fixes gh-1232
This commit is contained in:
Marcin Grzejszczak
2019-04-10 15:02:14 +02:00
parent 32b8cbecea
commit 7f0d4f1dc3
7 changed files with 1652 additions and 11 deletions

View File

@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
@@ -36,6 +37,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.ReflectionUtils;
/**
@@ -133,6 +135,22 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor {
return createThreadPoolTaskExecutorProxy(bean, cglibProxy, executor);
}
private ProxyFactoryBean wrapThreadPoolTaskScheduler(Object bean) {
boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers());
boolean cglibProxy = !classFinal;
ThreadPoolTaskScheduler executor = (ThreadPoolTaskScheduler) bean;
return proxyFactoryBean(bean, cglibProxy, executor,
createThreadPoolTaskSchedulerProxy(executor));
}
private ProxyFactoryBean wrapScheduledThreadPoolExecutor(Object bean) {
boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers());
boolean cglibProxy = !classFinal;
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) bean;
return proxyFactoryBean(bean, cglibProxy, executor,
createScheduledThreadPoolExecutorProxy(executor));
}
private Object wrapExecutorService(Object bean) {
boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers());
boolean cglibProxy = !classFinal;
@@ -158,6 +176,17 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor {
() -> new LazyTraceThreadPoolTaskExecutor(this.beanFactory, executor));
}
Supplier createThreadPoolTaskSchedulerProxy(ThreadPoolTaskScheduler executor) {
return () -> new LazyTraceThreadPoolTaskScheduler(this.beanFactory, executor);
}
Supplier createScheduledThreadPoolExecutorProxy(
ScheduledThreadPoolExecutor executor) {
return () -> new LazyTraceScheduledThreadPoolExecutor(executor.getCorePoolSize(),
executor.getThreadFactory(), executor.getRejectedExecutionHandler(),
this.beanFactory, executor);
}
Object createExecutorServiceProxy(Object bean, boolean cglibProxy,
ExecutorService executor) {
return getProxiedObject(bean, cglibProxy, executor,
@@ -172,6 +201,47 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor {
private Object getProxiedObject(Object bean, boolean cglibProxy, Executor executor,
Supplier<Executor> supplier) {
ProxyFactoryBean factory = proxyFactoryBean(bean, cglibProxy, executor, supplier);
try {
return getObject(factory);
}
catch (Exception ex) {
if (log.isDebugEnabled()) {
log.debug(
"Exception occurred while trying to get a proxy. Will fallback to a different implementation",
ex);
}
try {
if (bean instanceof ThreadPoolTaskScheduler) {
if (log.isDebugEnabled()) {
log.debug(
"Will wrap ThreadPoolTaskScheduler in its tracing representation due to previous errors");
}
return createThreadPoolTaskSchedulerProxy(
(ThreadPoolTaskScheduler) bean).get();
}
else if (bean instanceof ScheduledThreadPoolExecutor) {
if (log.isDebugEnabled()) {
log.debug(
"Will wrap ScheduledThreadPoolExecutor in its tracing representation due to previous errors");
}
return createScheduledThreadPoolExecutorProxy(
(ScheduledThreadPoolExecutor) bean).get();
}
}
catch (Exception ex2) {
if (log.isDebugEnabled()) {
log.debug(
"Fallback for special wrappers failed, will try the tracing representation instead",
ex2);
}
}
return supplier.get();
}
}
private ProxyFactoryBean proxyFactoryBean(Object bean, boolean cglibProxy,
Executor executor, Supplier<Executor> supplier) {
ProxyFactoryBean factory = new ProxyFactoryBean();
factory.setProxyTargetClass(cglibProxy);
factory.addAdvice(
@@ -182,17 +252,7 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor {
}
});
factory.setTarget(bean);
try {
return getObject(factory);
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(
"Exception occurred while trying to get a proxy. Will fallback to a different implementation",
e);
}
return supplier.get();
}
return factory;
}
Object getObject(ProxyFactoryBean factory) {

View File

@@ -0,0 +1,570 @@
/*
* 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
*
* 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.async;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import brave.Tracing;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.util.ReflectionUtils;
/**
* Trace representation of {@link ScheduledThreadPoolExecutor}. Should be used only * as
* last resort, when any other approaches fail.
*
* @author Marcin Grzejszczak
* @since 2.0.4
*/
// TODO: Think of a better solution than this
class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
private static final Log log = LogFactory
.getLog(LazyTraceScheduledThreadPoolExecutor.class);
private final BeanFactory beanFactory;
private final ScheduledThreadPoolExecutor delegate;
private final Method decorateTaskRunnable;
private final Method decorateTaskCallable;
private final Method finalize;
private final Method beforeExecute;
private final Method afterExecute;
private final Method terminated;
private final Method newTaskForRunnable;
private final Method newTaskForCallable;
private Tracing tracing;
private SpanNamer spanNamer;
LazyTraceScheduledThreadPoolExecutor(int corePoolSize, BeanFactory beanFactory,
ScheduledThreadPoolExecutor delegate) {
super(corePoolSize);
this.beanFactory = beanFactory;
this.delegate = delegate;
this.decorateTaskRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTask", Runnable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskRunnable);
this.decorateTaskCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTaskCallable", Callable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskCallable);
this.finalize = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"finalize", null);
makeAccessibleIfNotNull(this.finalize);
this.beforeExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"beforeExecute", null);
makeAccessibleIfNotNull(this.beforeExecute);
this.afterExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"afterExecute", null);
makeAccessibleIfNotNull(this.afterExecute);
this.terminated = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"terminated", null);
makeAccessibleIfNotNull(this.terminated);
this.newTaskForRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Runnable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForRunnable);
this.newTaskForCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Callable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForCallable);
}
private void makeAccessibleIfNotNull(Method method) {
if (method != null) {
ReflectionUtils.makeAccessible(method);
}
}
LazyTraceScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory,
BeanFactory beanFactory, ScheduledThreadPoolExecutor delegate) {
super(corePoolSize, threadFactory);
this.beanFactory = beanFactory;
this.delegate = delegate;
this.decorateTaskRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTask", Runnable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskRunnable);
this.decorateTaskCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTaskCallable", Callable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskCallable);
this.finalize = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"finalize");
makeAccessibleIfNotNull(this.finalize);
this.beforeExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"beforeExecute");
makeAccessibleIfNotNull(this.beforeExecute);
this.afterExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"afterExecute", null);
makeAccessibleIfNotNull(this.afterExecute);
this.terminated = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"terminated", null);
makeAccessibleIfNotNull(this.terminated);
this.newTaskForRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Runnable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForRunnable);
this.newTaskForCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Callable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForCallable);
}
LazyTraceScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler, BeanFactory beanFactory,
ScheduledThreadPoolExecutor delegate) {
super(corePoolSize, handler);
this.beanFactory = beanFactory;
this.delegate = delegate;
this.decorateTaskRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTask", Runnable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskRunnable);
this.decorateTaskCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTaskCallable", Callable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskCallable);
this.finalize = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"finalize", null);
makeAccessibleIfNotNull(this.finalize);
this.beforeExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"beforeExecute", null);
makeAccessibleIfNotNull(this.beforeExecute);
this.afterExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"afterExecute", null);
makeAccessibleIfNotNull(this.afterExecute);
this.terminated = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"terminated", null);
makeAccessibleIfNotNull(this.terminated);
this.newTaskForRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Runnable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForRunnable);
this.newTaskForCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Callable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForCallable);
}
LazyTraceScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory,
RejectedExecutionHandler handler, BeanFactory beanFactory,
ScheduledThreadPoolExecutor delegate) {
super(corePoolSize, threadFactory, handler);
this.beanFactory = beanFactory;
this.delegate = delegate;
this.decorateTaskRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTask", Runnable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskRunnable);
this.decorateTaskCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "decorateTaskCallable", Callable.class,
RunnableScheduledFuture.class);
makeAccessibleIfNotNull(this.decorateTaskCallable);
this.finalize = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"finalize", null);
makeAccessibleIfNotNull(this.finalize);
this.beforeExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"beforeExecute", null);
makeAccessibleIfNotNull(this.beforeExecute);
this.afterExecute = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"afterExecute", null);
makeAccessibleIfNotNull(this.afterExecute);
this.terminated = ReflectionUtils.findMethod(ScheduledThreadPoolExecutor.class,
"terminated");
makeAccessibleIfNotNull(this.terminated);
this.newTaskForRunnable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Runnable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForRunnable);
this.newTaskForCallable = ReflectionUtils.findMethod(
ScheduledThreadPoolExecutor.class, "newTaskFor", Callable.class,
Object.class);
makeAccessibleIfNotNull(this.newTaskForCallable);
}
@Override
@SuppressWarnings("unchecked")
public <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable,
RunnableScheduledFuture<V> task) {
return (RunnableScheduledFuture<V>) ReflectionUtils.invokeMethod(
this.decorateTaskRunnable, this.delegate,
new TraceRunnable(tracing(), spanNamer(), runnable), task);
}
@Override
@SuppressWarnings("unchecked")
public <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable,
RunnableScheduledFuture<V> task) {
return (RunnableScheduledFuture<V>) ReflectionUtils.invokeMethod(
this.decorateTaskCallable, this.delegate,
new TraceCallable<>(tracing(), spanNamer(), callable), task);
}
@Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), command),
delay, unit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay,
TimeUnit unit) {
return this.delegate.schedule(
new TraceCallable<>(tracing(), spanNamer(), callable), delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
long period, TimeUnit unit) {
return this.delegate.scheduleAtFixedRate(
new TraceRunnable(tracing(), spanNamer(), command), initialDelay, period,
unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
long delay, TimeUnit unit) {
return this.delegate.scheduleWithFixedDelay(
new TraceRunnable(tracing(), spanNamer(), command), initialDelay, delay,
unit);
}
@Override
public void execute(Runnable command) {
this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), command));
}
@Override
public Future<?> submit(Runnable task) {
return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task));
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task),
result);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return this.delegate.submit(new TraceCallable<>(tracing(), spanNamer(), task));
}
@Override
public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) {
this.delegate.setContinueExistingPeriodicTasksAfterShutdownPolicy(value);
}
@Override
public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() {
return this.delegate.getContinueExistingPeriodicTasksAfterShutdownPolicy();
}
@Override
public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) {
this.delegate.setExecuteExistingDelayedTasksAfterShutdownPolicy(value);
}
@Override
public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() {
return this.delegate.getExecuteExistingDelayedTasksAfterShutdownPolicy();
}
@Override
public void setRemoveOnCancelPolicy(boolean value) {
this.delegate.setRemoveOnCancelPolicy(value);
}
@Override
public boolean getRemoveOnCancelPolicy() {
return this.delegate.getRemoveOnCancelPolicy();
}
@Override
public void shutdown() {
this.delegate.shutdown();
}
@Override
public List<Runnable> shutdownNow() {
return this.delegate.shutdownNow();
}
@Override
public BlockingQueue<Runnable> getQueue() {
return this.delegate.getQueue();
}
@Override
public boolean isShutdown() {
return this.delegate.isShutdown();
}
@Override
public boolean isTerminating() {
return this.delegate.isTerminating();
}
@Override
public boolean isTerminated() {
return this.delegate.isTerminated();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return this.delegate.awaitTermination(timeout, unit);
}
@Override
@Deprecated
public void finalize() {
ReflectionUtils.invokeMethod(this.finalize, this.delegate);
}
@Override
public void setThreadFactory(ThreadFactory threadFactory) {
this.delegate.setThreadFactory(threadFactory);
}
@Override
public ThreadFactory getThreadFactory() {
return this.delegate.getThreadFactory();
}
@Override
public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
this.delegate.setRejectedExecutionHandler(handler);
}
@Override
public RejectedExecutionHandler getRejectedExecutionHandler() {
return this.delegate.getRejectedExecutionHandler();
}
@Override
public void setCorePoolSize(int corePoolSize) {
this.delegate.setCorePoolSize(corePoolSize);
}
@Override
public int getCorePoolSize() {
return this.delegate.getCorePoolSize();
}
@Override
public boolean prestartCoreThread() {
return this.delegate.prestartCoreThread();
}
@Override
public int prestartAllCoreThreads() {
return this.delegate.prestartAllCoreThreads();
}
@Override
public boolean allowsCoreThreadTimeOut() {
return this.delegate.allowsCoreThreadTimeOut();
}
@Override
public void allowCoreThreadTimeOut(boolean value) {
this.delegate.allowCoreThreadTimeOut(value);
}
@Override
public void setMaximumPoolSize(int maximumPoolSize) {
this.delegate.setMaximumPoolSize(maximumPoolSize);
}
@Override
public int getMaximumPoolSize() {
return this.delegate.getMaximumPoolSize();
}
@Override
public void setKeepAliveTime(long time, TimeUnit unit) {
this.delegate.setKeepAliveTime(time, unit);
}
@Override
public long getKeepAliveTime(TimeUnit unit) {
return this.delegate.getKeepAliveTime(unit);
}
@Override
public boolean remove(Runnable task) {
return this.delegate.remove(task);
}
@Override
public void purge() {
this.delegate.purge();
}
@Override
public int getPoolSize() {
return this.delegate.getPoolSize();
}
@Override
public int getActiveCount() {
return this.delegate.getActiveCount();
}
@Override
public int getLargestPoolSize() {
return this.delegate.getLargestPoolSize();
}
@Override
public long getTaskCount() {
return this.delegate.getTaskCount();
}
@Override
public long getCompletedTaskCount() {
return this.delegate.getCompletedTaskCount();
}
@Override
public String toString() {
return this.delegate.toString();
}
@Override
public void beforeExecute(Thread t, Runnable r) {
ReflectionUtils.invokeMethod(this.beforeExecute, this.delegate, t,
new TraceRunnable(tracing(), spanNamer(), r));
}
@Override
public void afterExecute(Runnable r, Throwable t) {
ReflectionUtils.invokeMethod(this.afterExecute, this.delegate,
new TraceRunnable(tracing(), spanNamer(), r), t);
}
@Override
public void terminated() {
ReflectionUtils.invokeMethod(this.terminated, this.delegate);
}
@Override
@SuppressWarnings("unchecked")
public <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return (RunnableFuture<T>) ReflectionUtils.invokeMethod(this.newTaskForRunnable,
this.delegate, new TraceRunnable(tracing(), spanNamer(), runnable),
value);
}
@Override
@SuppressWarnings("unchecked")
public <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return (RunnableFuture<T>) ReflectionUtils.invokeMethod(this.newTaskForCallable,
this.delegate, new TraceCallable<>(tracing(), spanNamer(), callable));
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
return this.delegate.invokeAny(wrapCallableCollection(tasks));
}
private <T> Collection<? extends Callable<T>> wrapCallableCollection(
Collection<? extends Callable<T>> tasks) {
List<Callable<T>> ts = new ArrayList<>();
for (Callable<T> task : tasks) {
if (!(task instanceof TraceCallable)) {
ts.add(new TraceCallable<>(tracing(), spanNamer(), task));
}
}
return ts;
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout,
TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return this.delegate.invokeAny(wrapCallableCollection(tasks), timeout, unit);
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
return this.delegate.invokeAll(wrapCallableCollection(tasks));
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) throws InterruptedException {
return this.delegate.invokeAll(wrapCallableCollection(tasks), timeout, unit);
}
private Tracing tracing() {
if (this.tracing == null) {
this.tracing = this.beanFactory.getBean(Tracing.class);
}
return this.tracing;
}
private SpanNamer spanNamer() {
if (this.spanNamer == null) {
try {
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
}
catch (NoSuchBeanDefinitionException e) {
log.warn(
"SpanNamer bean not found - will provide a manually created instance");
return new DefaultSpanNamer();
}
}
return this.spanNamer;
}
}

View File

@@ -0,0 +1,439 @@
/*
* 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.async;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import brave.Tracing;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.lang.Nullable;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.CustomizableThreadCreator;
import org.springframework.util.ErrorHandler;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Trace representation of {@link ThreadPoolTaskScheduler}. Should be used only as last
* resort, when any other approaches fail.
*
* @author Marcin Grzejszczak
* @since 2.0.4
*/
// TODO: Think of a better solution than this
class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler {
private static final Log log = LogFactory
.getLog(LazyTraceThreadPoolTaskScheduler.class);
private final BeanFactory beanFactory;
private final ThreadPoolTaskScheduler delegate;
private final Method initializeExecutor;
private final Method createExecutor;
private final Method cancelRemainingTask;
private final Method nextThreadName;
private final Method getDefaultThreadNamePrefix;
private Tracing tracing;
private SpanNamer spanNamer;
LazyTraceThreadPoolTaskScheduler(BeanFactory beanFactory,
ThreadPoolTaskScheduler delegate) {
this.beanFactory = beanFactory;
this.delegate = delegate;
this.initializeExecutor = ReflectionUtils
.findMethod(ThreadPoolTaskScheduler.class, "initializeExecutor", null);
makeAccessibleIfNotNull(this.initializeExecutor);
this.createExecutor = ReflectionUtils.findMethod(ThreadPoolTaskScheduler.class,
"createExecutor", null);
makeAccessibleIfNotNull(this.createExecutor);
this.cancelRemainingTask = ReflectionUtils
.findMethod(ThreadPoolTaskScheduler.class, "cancelRemainingTask", null);
makeAccessibleIfNotNull(this.cancelRemainingTask);
this.nextThreadName = ReflectionUtils.findMethod(ThreadPoolTaskScheduler.class,
"nextThreadName", null);
makeAccessibleIfNotNull(this.nextThreadName);
this.getDefaultThreadNamePrefix = ReflectionUtils.findMethod(
CustomizableThreadCreator.class, "getDefaultThreadNamePrefix", null);
makeAccessibleIfNotNull(this.getDefaultThreadNamePrefix);
}
private void makeAccessibleIfNotNull(Method method) {
if (method != null) {
ReflectionUtils.makeAccessible(method);
}
}
@Override
public void setPoolSize(int poolSize) {
this.delegate.setPoolSize(poolSize);
}
@Override
public void setRemoveOnCancelPolicy(boolean removeOnCancelPolicy) {
this.delegate.setRemoveOnCancelPolicy(removeOnCancelPolicy);
}
@Override
public void setErrorHandler(ErrorHandler errorHandler) {
this.delegate.setErrorHandler(errorHandler);
}
@Override
public ExecutorService initializeExecutor(ThreadFactory threadFactory,
RejectedExecutionHandler rejectedExecutionHandler) {
ExecutorService executorService = (ExecutorService) ReflectionUtils.invokeMethod(
this.initializeExecutor, this.delegate, traceThreadFactory(threadFactory),
rejectedExecutionHandler);
if (executorService instanceof TraceableScheduledExecutorService) {
return executorService;
}
return new TraceableExecutorService(this.beanFactory, executorService);
}
private ThreadFactory traceThreadFactory(ThreadFactory threadFactory) {
return r -> threadFactory.newThread(new TraceRunnable(tracing(), spanNamer(), r));
}
@Override
public ScheduledExecutorService createExecutor(int poolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler rejectedExecutionHandler) {
ScheduledExecutorService executorService = (ScheduledExecutorService) ReflectionUtils
.invokeMethod(this.createExecutor, this.delegate, poolSize,
traceThreadFactory(threadFactory), rejectedExecutionHandler);
if (executorService instanceof TraceableScheduledExecutorService) {
return executorService;
}
return new TraceableScheduledExecutorService(this.beanFactory, executorService);
}
@Override
public ScheduledExecutorService getScheduledExecutor() throws IllegalStateException {
ScheduledExecutorService executor = this.delegate.getScheduledExecutor();
return executor instanceof TraceableScheduledExecutorService ? executor
: new TraceableScheduledExecutorService(this.beanFactory, executor);
}
@Override
public ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
throws IllegalStateException {
ScheduledThreadPoolExecutor executor = this.delegate
.getScheduledThreadPoolExecutor();
if (executor instanceof LazyTraceScheduledThreadPoolExecutor) {
return executor;
}
return new LazyTraceScheduledThreadPoolExecutor(executor.getCorePoolSize(),
executor.getThreadFactory(), executor.getRejectedExecutionHandler(),
this.beanFactory, executor);
}
@Override
public int getPoolSize() {
return this.delegate.getPoolSize();
}
@Override
public boolean isRemoveOnCancelPolicy() {
return this.delegate.isRemoveOnCancelPolicy();
}
@Override
public int getActiveCount() {
return this.delegate.getActiveCount();
}
@Override
public void execute(Runnable task) {
this.delegate.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), task),
startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
return this.delegate.submit(new TraceRunnable(tracing(), spanNamer(), task));
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return this.delegate.submit(new TraceCallable<>(tracing(), spanNamer(), task));
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
return this.delegate
.submitListenable(new TraceRunnable(tracing(), spanNamer(), task));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return this.delegate
.submitListenable(new TraceCallable<>(tracing(), spanNamer(), task));
}
@Override
public void cancelRemainingTask(Runnable task) {
ReflectionUtils.invokeMethod(this.cancelRemainingTask, this.delegate,
new TraceRunnable(tracing(), spanNamer(), task));
}
@Override
public boolean prefersShortLivedTasks() {
return this.delegate.prefersShortLivedTasks();
}
@Override
@Nullable
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task),
trigger);
}
@Override
public ScheduledFuture<?> schedule(Runnable task, Date startTime) {
return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task),
startTime);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime,
long period) {
return this.delegate.scheduleAtFixedRate(
new TraceRunnable(tracing(), spanNamer(), task), startTime, period);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
return this.delegate.scheduleAtFixedRate(
new TraceRunnable(tracing(), spanNamer(), task), period);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime,
long delay) {
return this.delegate.scheduleWithFixedDelay(
new TraceRunnable(tracing(), spanNamer(), task), startTime, delay);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) {
return this.delegate.scheduleWithFixedDelay(
new TraceRunnable(tracing(), spanNamer(), task), delay);
}
@Override
public void setThreadFactory(ThreadFactory threadFactory) {
this.delegate.setThreadFactory(threadFactory);
}
@Override
public void setThreadNamePrefix(String threadNamePrefix) {
this.delegate.setThreadNamePrefix(threadNamePrefix);
}
@Override
public void setRejectedExecutionHandler(
RejectedExecutionHandler rejectedExecutionHandler) {
this.delegate.setRejectedExecutionHandler(rejectedExecutionHandler);
}
@Override
public void setWaitForTasksToCompleteOnShutdown(
boolean waitForJobsToCompleteOnShutdown) {
this.delegate
.setWaitForTasksToCompleteOnShutdown(waitForJobsToCompleteOnShutdown);
}
@Override
public void setAwaitTerminationSeconds(int awaitTerminationSeconds) {
this.delegate.setAwaitTerminationSeconds(awaitTerminationSeconds);
}
@Override
public void setBeanName(String name) {
this.delegate.setBeanName(name);
}
@Override
public void afterPropertiesSet() {
this.delegate.afterPropertiesSet();
}
@Override
public void initialize() {
this.delegate.initialize();
}
@Override
public void destroy() {
this.delegate.destroy();
}
@Override
public void shutdown() {
this.delegate.shutdown();
}
@Override
public Thread newThread(Runnable runnable) {
return this.delegate.newThread(runnable);
}
@Override
public String getThreadNamePrefix() {
return this.delegate.getThreadNamePrefix();
}
@Override
public void setThreadPriority(int threadPriority) {
this.delegate.setThreadPriority(threadPriority);
}
@Override
public int getThreadPriority() {
return this.delegate.getThreadPriority();
}
@Override
public void setDaemon(boolean daemon) {
this.delegate.setDaemon(daemon);
}
@Override
public boolean isDaemon() {
return this.delegate.isDaemon();
}
@Override
public void setThreadGroupName(String name) {
this.delegate.setThreadGroupName(name);
}
@Override
public void setThreadGroup(ThreadGroup threadGroup) {
this.delegate.setThreadGroup(threadGroup);
}
@Override
@Nullable
public ThreadGroup getThreadGroup() {
return this.delegate.getThreadGroup();
}
@Override
public Thread createThread(Runnable runnable) {
return this.delegate.createThread(runnable);
}
@Override
public String nextThreadName() {
return (String) ReflectionUtils.invokeMethod(this.nextThreadName, this.delegate);
}
@Override
public String getDefaultThreadNamePrefix() {
if (this.delegate == null) {
return super.getDefaultThreadNamePrefix();
}
return (String) ReflectionUtils.invokeMethod(this.getDefaultThreadNamePrefix,
this.delegate);
}
@Override
public ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
return this.delegate.schedule(new TraceRunnable(tracing(), spanNamer(), task),
startTime);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime,
Duration period) {
return this.delegate.scheduleAtFixedRate(
new TraceRunnable(tracing(), spanNamer(), task), startTime, period);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
return this.delegate.scheduleAtFixedRate(
new TraceRunnable(tracing(), spanNamer(), task), period);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime,
Duration delay) {
return this.delegate.scheduleWithFixedDelay(
new TraceRunnable(tracing(), spanNamer(), task), startTime, delay);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
return this.delegate.scheduleWithFixedDelay(
new TraceRunnable(tracing(), spanNamer(), task), delay);
}
private Tracing tracing() {
if (this.tracing == null) {
this.tracing = this.beanFactory.getBean(Tracing.class);
}
return this.tracing;
}
private SpanNamer spanNamer() {
if (this.spanNamer == null) {
try {
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
}
catch (NoSuchBeanDefinitionException e) {
log.warn(
"SpanNamer bean not found - will provide a manually created instance");
return new DefaultSpanNamer();
}
}
return this.spanNamer;
}
}

View File

@@ -60,6 +60,9 @@ public class TraceSchedulingAspect {
@Around("execution (@org.springframework.scheduling.annotation.Scheduled * *.*(..))")
public Object traceBackgroundThread(final ProceedingJoinPoint pjp) throws Throwable {
if (this.skipPattern.matcher(pjp.getTarget().getClass().getName()).matches()) {
// we might have a span in context due to wrapping of runnables
// we want to clear that context
this.tracer.withSpanInScope(null);
return pjp.proceed();
}
String spanName = SpanNameUtil.toLowerHyphen(pjp.getSignature().getName());

View File

@@ -0,0 +1,462 @@
/*
* 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.async;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import brave.Tracing;
import brave.propagation.StrictScopeDecorator;
import brave.propagation.ThreadLocalCurrentTraceContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.BDDMockito;
import org.mockito.Mock;
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.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.ErrorHandler;
@RunWith(MockitoJUnitRunner.class)
public class LazyTraceThreadPoolTaskSchedulerTests {
Tracing tracing = Tracing.newBuilder()
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
.addScopeDecorator(StrictScopeDecorator.create()).build())
.build();
@Mock
BeanFactory beanFactory;
@Mock
ThreadPoolTaskScheduler delegate;
LazyTraceThreadPoolTaskScheduler executor;
@Before
public void setup() {
this.executor = new LazyTraceThreadPoolTaskScheduler(beanFactory(),
this.delegate);
}
BeanFactory beanFactory() {
BDDMockito.given(this.beanFactory.getBean(Tracing.class))
.willReturn(this.tracing);
BDDMockito.given(this.beanFactory.getBean(SpanNamer.class))
.willReturn(new DefaultSpanNamer());
ContextRefreshedListenerAccessor.set(this.beanFactory, true);
return this.beanFactory;
}
@Test
public void setPoolSize() {
this.executor.setPoolSize(10);
BDDMockito.then(this.delegate).should().setPoolSize(10);
}
@Test
public void setRemoveOnCancelPolicy() {
this.executor.setRemoveOnCancelPolicy(true);
BDDMockito.then(this.delegate).should().setRemoveOnCancelPolicy(true);
}
@Test
public void setErrorHandler() {
ErrorHandler handler = (throwable) -> {
};
this.executor.setErrorHandler(handler);
BDDMockito.then(this.delegate).should().setErrorHandler(handler);
}
@Test
public void getScheduledExecutor() {
this.executor.getScheduledExecutor();
BDDMockito.then(this.delegate).should().getScheduledExecutor();
}
@Test
public void getPoolSize() {
this.executor.getPoolSize();
BDDMockito.then(this.delegate).should().getPoolSize();
}
@Test
public void isRemoveOnCancelPolicy() {
this.executor.setRemoveOnCancelPolicy(true);
BDDMockito.then(this.delegate).should().setRemoveOnCancelPolicy(true);
}
@Test
public void getActiveCount() {
this.executor.getActiveCount();
BDDMockito.then(this.delegate).should().getActiveCount();
}
@Test
public void execute() {
Runnable r = () -> {
};
this.executor.execute(r);
BDDMockito.then(this.delegate).should().execute(r);
}
@Test
public void execute1() {
Runnable r = () -> {
};
this.executor.execute(r, 10L);
BDDMockito.then(this.delegate).should()
.execute(BDDMockito.any(TraceRunnable.class), BDDMockito.eq(10L));
}
@Test
public void submit() {
Runnable c = () -> {
};
this.executor.submit(c);
BDDMockito.then(this.delegate).should()
.submit(BDDMockito.any(TraceRunnable.class));
}
@Test
public void submit1() {
Callable c = () -> null;
this.executor.submit(c);
BDDMockito.then(this.delegate).should()
.submit(BDDMockito.any(TraceCallable.class));
}
@Test
public void submitListenable() {
Runnable c = () -> {
};
this.executor.submitListenable(c);
BDDMockito.then(this.delegate).should()
.submitListenable(BDDMockito.any(TraceRunnable.class));
}
@Test
public void submitListenable1() {
Callable c = () -> null;
this.executor.submitListenable(c);
BDDMockito.then(this.delegate).should()
.submitListenable(BDDMockito.any(TraceCallable.class));
}
@Test
public void prefersShortLivedTasks() {
this.executor.prefersShortLivedTasks();
BDDMockito.then(this.delegate).should().prefersShortLivedTasks();
}
@Test
public void schedule() {
Runnable c = () -> {
};
Trigger trigger = triggerContext -> null;
this.executor.schedule(c, trigger);
BDDMockito.then(this.delegate).should()
.schedule(BDDMockito.any(TraceRunnable.class), BDDMockito.eq(trigger));
}
@Test
public void schedule1() {
Runnable c = () -> {
};
Date date = new Date();
this.executor.schedule(c, date);
BDDMockito.then(this.delegate).should()
.schedule(BDDMockito.any(TraceRunnable.class), BDDMockito.eq(date));
}
@Test
public void scheduleAtFixedRate() {
Runnable c = () -> {
};
Date date = new Date();
this.executor.scheduleAtFixedRate(c, date, 10L);
BDDMockito.then(this.delegate).should().scheduleAtFixedRate(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(date),
BDDMockito.eq(10L));
}
@Test
public void scheduleAtFixedRate1() {
Runnable c = () -> {
};
this.executor.scheduleAtFixedRate(c, 10L);
BDDMockito.then(this.delegate).should().scheduleAtFixedRate(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(10L));
}
@Test
public void scheduleWithFixedDelay() {
Runnable c = () -> {
};
Date date = new Date();
this.executor.scheduleWithFixedDelay(c, date, 10L);
BDDMockito.then(this.delegate).should().scheduleWithFixedDelay(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(date),
BDDMockito.eq(10L));
}
@Test
public void scheduleWithFixedDelay1() {
Runnable c = () -> {
};
this.executor.scheduleWithFixedDelay(c, 10L);
BDDMockito.then(this.delegate).should().scheduleWithFixedDelay(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(10L));
}
@Test
public void scheduleWithFixedDelay2() {
Runnable c = () -> {
};
Instant instant = Instant.now();
Duration duration = Duration.ZERO;
this.executor.scheduleWithFixedDelay(c, instant, duration);
BDDMockito.then(this.delegate).should().scheduleWithFixedDelay(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(instant),
BDDMockito.eq(duration));
}
@Test
public void scheduleWithFixedDelay3() {
Runnable c = () -> {
};
Duration duration = Duration.ZERO;
this.executor.scheduleWithFixedDelay(c, duration);
BDDMockito.then(this.delegate).should().scheduleWithFixedDelay(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(duration));
}
@Test
public void setThreadFactory() {
ThreadFactory threadFactory = r -> null;
this.executor.setThreadFactory(threadFactory);
BDDMockito.then(this.delegate).should().setThreadFactory(threadFactory);
}
@Test
public void setThreadNamePrefix() {
this.executor.setThreadNamePrefix("foo");
BDDMockito.then(this.delegate).should().setThreadNamePrefix("foo");
}
@Test
public void setRejectedExecutionHandler() {
RejectedExecutionHandler handler = (r, executor1) -> {
};
this.executor.setRejectedExecutionHandler(handler);
BDDMockito.then(this.delegate).should().setRejectedExecutionHandler(handler);
}
@Test
public void setWaitForTasksToCompleteOnShutdown() {
this.executor.setWaitForTasksToCompleteOnShutdown(true);
BDDMockito.then(this.delegate).should().setWaitForTasksToCompleteOnShutdown(true);
}
@Test
public void setAwaitTerminationSeconds() {
this.executor.setAwaitTerminationSeconds(10);
BDDMockito.then(this.delegate).should().setAwaitTerminationSeconds(10);
}
@Test
public void setBeanName() {
this.executor.setBeanName("foo");
BDDMockito.then(this.delegate).should().setBeanName("foo");
}
@Test
public void afterPropertiesSet() {
this.executor.afterPropertiesSet();
BDDMockito.then(this.delegate).should().afterPropertiesSet();
}
@Test
public void initialize() {
this.executor.initialize();
BDDMockito.then(this.delegate).should().initialize();
}
@Test
public void destroy() {
this.executor.destroy();
BDDMockito.then(this.delegate).should().destroy();
}
@Test
public void shutdown() {
this.executor.shutdown();
BDDMockito.then(this.delegate).should().shutdown();
}
@Test
public void newThread() {
Runnable runnable = () -> {
};
this.executor.newThread(runnable);
BDDMockito.then(this.delegate).should().newThread(runnable);
}
@Test
public void getThreadNamePrefix() {
this.executor.getThreadNamePrefix();
BDDMockito.then(this.delegate).should().getThreadNamePrefix();
}
@Test
public void setThreadPriority() {
this.executor.setThreadPriority(10);
BDDMockito.then(this.delegate).should().setThreadPriority(10);
}
@Test
public void getThreadPriority() {
this.executor.getThreadPriority();
BDDMockito.then(this.delegate).should().getThreadPriority();
}
@Test
public void setDaemon() {
this.executor.setDaemon(true);
BDDMockito.then(this.delegate).should().setDaemon(true);
}
@Test
public void isDaemon() {
this.executor.isDaemon();
BDDMockito.then(this.delegate).should().isDaemon();
}
@Test
public void setThreadGroupName() {
this.executor.setThreadGroupName("foo");
BDDMockito.then(this.delegate).should().setThreadGroupName("foo");
}
@Test
public void setThreadGroup() {
ThreadGroup threadGroup = new ThreadGroup("foo");
this.executor.setThreadGroup(threadGroup);
BDDMockito.then(this.delegate).should().setThreadGroup(threadGroup);
}
@Test
public void getThreadGroup() {
this.executor.getThreadGroup();
BDDMockito.then(this.delegate).should().getThreadGroup();
}
@Test
public void createThread() {
Runnable r = () -> {
};
this.executor.createThread(r);
BDDMockito.then(this.delegate).should().createThread(r);
}
@Test
public void schedule2() {
Runnable r = () -> {
};
Instant instant = Instant.now();
this.executor.schedule(r, instant);
BDDMockito.then(this.delegate).should()
.schedule(BDDMockito.any(TraceRunnable.class), BDDMockito.eq(instant));
}
@Test
public void scheduleAtFixedRate2() {
Runnable r = () -> {
};
Instant instant = Instant.now();
Duration duration = Duration.ZERO;
this.executor.scheduleAtFixedRate(r, instant, duration);
BDDMockito.then(this.delegate).should().scheduleAtFixedRate(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(instant),
BDDMockito.eq(duration));
}
@Test
public void scheduleAtFixedRate3() {
Runnable r = () -> {
};
Duration duration = Duration.ZERO;
this.executor.scheduleAtFixedRate(r, duration);
BDDMockito.then(this.delegate).should().scheduleAtFixedRate(
BDDMockito.any(TraceRunnable.class), BDDMockito.eq(duration));
}
}

View File

@@ -20,6 +20,7 @@ import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicReference;
import brave.Span;
@@ -45,6 +46,7 @@ import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -176,6 +178,58 @@ public class Issue410Tests {
then(this.tracer.currentSpan()).isNull();
}
/**
* Related to issue #1232
*/
@Test
public void should_pass_tracing_info_for_completable_futures_with_threadPoolTaskScheduler() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/threadPoolTaskScheduler",
String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
/**
* Related to issue #1232
*/
@Test
public void should_pass_tracing_info_for_completable_futures_with_scheduledThreadPoolExecutor() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/scheduledThreadPoolExecutor",
String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
@@ -203,6 +257,18 @@ class AppConfig {
return executor;
}
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.initialize();
return executor;
}
@Bean
public ScheduledThreadPoolExecutor scheduledThreadPoolExecutor() {
return new ScheduledThreadPoolExecutor(10);
}
}
@Component
@@ -224,6 +290,12 @@ class AsyncTask {
@Autowired
BeanFactory beanFactory;
@Autowired
ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
private AtomicReference<Span> span = new AtomicReference<>();
@Async("poolTaskExecutor")
@@ -290,6 +362,26 @@ class AsyncTask {
return this.span.get();
}
public Span scheduledThreadPoolExecutor()
throws ExecutionException, InterruptedException {
log.info("This task is running with ScheduledThreadPoolExecutor");
this.scheduledThreadPoolExecutor.submit(() -> {
log.info("Hello from runnable");
AsyncTask.this.span.set(AsyncTask.this.tracer.currentSpan());
}).get();
return this.span.get();
}
public Span threadPoolTaskScheduler()
throws ExecutionException, InterruptedException {
log.info("This task is running with ThreadPoolTaskScheduler");
this.threadPoolTaskScheduler.submit(() -> {
log.info("Hello from runnable");
AsyncTask.this.span.set(AsyncTask.this.tracer.currentSpan());
}).get();
return this.span.get();
}
public AtomicReference<Span> getSpan() {
return this.span;
}
@@ -335,6 +427,20 @@ class Application {
return this.asyncTask.taskScheduler().context().traceIdString();
}
@RequestMapping("/threadPoolTaskScheduler")
public String threadPoolTaskScheduler()
throws ExecutionException, InterruptedException {
log.info("Executing completable via ThreadPoolTaskScheduler");
return this.asyncTask.threadPoolTaskScheduler().context().traceIdString();
}
@RequestMapping("/scheduledThreadPoolExecutor")
public String scheduledThreadPoolExecutor()
throws ExecutionException, InterruptedException {
log.info("Executing completable via ScheduledThreadPoolExecutor");
return this.asyncTask.scheduledThreadPoolExecutor().context().traceIdString();
}
/**
* Related to issue #445.
* @return service bean

View File

@@ -68,6 +68,7 @@ public class TracingOnScheduledTests {
public void setup() {
this.beanWithScheduledMethod.clear();
this.beanWithScheduledMethodToBeIgnored.clear();
this.reporter.clear();
}
@Test