Add static wrappers for Executor / ExecutorService using current cont… (#2988)
* Add static wrappers for Executor / ExecutorService using current context at invocation time. * Missing javadoc * Cleanup * taskWrapping
This commit is contained in:
parent
5f32df7b9b
commit
691e24fc19
|
|
@ -99,6 +99,36 @@ public interface Context {
|
||||||
return ArrayBasedContext.root();
|
return ArrayBasedContext.root();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link Executor} which delegates to the provided {@code executor}, wrapping all
|
||||||
|
* invocations of {@link Executor#execute(Runnable)} with the {@linkplain Context#current()
|
||||||
|
* current context} at the time of invocation.
|
||||||
|
*
|
||||||
|
* <p>This is generally used to create an {@link Executor} which will forward the {@link Context}
|
||||||
|
* during an invocation to another thread. For example, you may use something like {@code Executor
|
||||||
|
* dbExecutor = Context.wrapTasks(threadPool)} to ensure calls like {@code dbExecutor.execute(()
|
||||||
|
* -> database.query())} have {@link Context} available on the thread executing database queries.
|
||||||
|
*/
|
||||||
|
static Executor taskWrapping(Executor executor) {
|
||||||
|
return command -> executor.execute(Context.current().wrap(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link ExecutorService} which delegates to the provided {@code executorService},
|
||||||
|
* wrapping all invocations of {@link ExecutorService} methods such as {@link
|
||||||
|
* ExecutorService#execute(Runnable)} or {@link ExecutorService#submit(Runnable)} with the
|
||||||
|
* {@linkplain Context#current() current context} at the time of invocation.
|
||||||
|
*
|
||||||
|
* <p>This is generally used to create an {@link ExecutorService} which will forward the {@link
|
||||||
|
* Context} during an invocation to another thread. For example, you may use something like {@code
|
||||||
|
* ExecutorService dbExecutor = Context.wrapTasks(threadPool)} to ensure calls like {@code
|
||||||
|
* dbExecutor.execute(() -> database.query())} have {@link Context} available on the thread
|
||||||
|
* executing database queries.
|
||||||
|
*/
|
||||||
|
static ExecutorService taskWrapping(ExecutorService executorService) {
|
||||||
|
return new CurrentContextExecutorService(executorService);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value stored in this {@link Context} for the given {@link ContextKey}, or {@code
|
* Returns the value stored in this {@link Context} for the given {@link ContextKey}, or {@code
|
||||||
* null} if there is no value for the key in this context.
|
* null} if there is no value for the key in this context.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
package io.opentelemetry.context;
|
package io.opentelemetry.context;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
@ -15,99 +14,61 @@ import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
class ContextExecutorService implements ExecutorService {
|
class ContextExecutorService extends ForwardingExecutorService {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final ExecutorService delegate;
|
|
||||||
|
|
||||||
ContextExecutorService(Context context, ExecutorService delegate) {
|
ContextExecutorService(Context context, ExecutorService delegate) {
|
||||||
|
super(delegate);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Context context() {
|
final Context context() {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutorService delegate() {
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
delegate.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Runnable> shutdownNow() {
|
|
||||||
return delegate.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isShutdown() {
|
|
||||||
return delegate.isShutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTerminated() {
|
|
||||||
return delegate.isTerminated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
|
||||||
return delegate.awaitTermination(timeout, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Future<T> submit(Callable<T> task) {
|
public <T> Future<T> submit(Callable<T> task) {
|
||||||
return delegate.submit(context.wrap(task));
|
return delegate().submit(context.wrap(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Future<T> submit(Runnable task, T result) {
|
public <T> Future<T> submit(Runnable task, T result) {
|
||||||
return delegate.submit(context.wrap(task), result);
|
return delegate().submit(context.wrap(task), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<?> submit(Runnable task) {
|
public Future<?> submit(Runnable task) {
|
||||||
return delegate.submit(context.wrap(task));
|
return delegate().submit(context.wrap(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
|
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
return delegate.invokeAll(wrap(tasks));
|
return delegate().invokeAll(wrap(context, tasks));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> List<Future<T>> invokeAll(
|
public <T> List<Future<T>> invokeAll(
|
||||||
Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
return delegate.invokeAll(wrap(tasks), timeout, unit);
|
return delegate().invokeAll(wrap(context, tasks), timeout, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
|
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
|
||||||
throws InterruptedException, ExecutionException {
|
throws InterruptedException, ExecutionException {
|
||||||
return delegate.invokeAny(wrap(tasks));
|
return delegate().invokeAny(wrap(context, tasks));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||||
throws InterruptedException, ExecutionException, TimeoutException {
|
throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
return delegate.invokeAny(wrap(tasks), timeout, unit);
|
return delegate().invokeAny(wrap(context, tasks), timeout, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Runnable command) {
|
public void execute(Runnable command) {
|
||||||
delegate.execute(context.wrap(command));
|
delegate().execute(context.wrap(command));
|
||||||
}
|
|
||||||
|
|
||||||
private <T> Collection<? extends Callable<T>> wrap(Collection<? extends Callable<T>> tasks) {
|
|
||||||
List<Callable<T>> wrapped = new ArrayList<>();
|
|
||||||
for (Callable<T> task : tasks) {
|
|
||||||
wrapped.add(context.wrap(task));
|
|
||||||
}
|
|
||||||
return wrapped;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.context;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
final class CurrentContextExecutorService extends ForwardingExecutorService {
|
||||||
|
|
||||||
|
CurrentContextExecutorService(ExecutorService delegate) {
|
||||||
|
super(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Future<T> submit(Callable<T> task) {
|
||||||
|
return delegate().submit(Context.current().wrap(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Future<T> submit(Runnable task, T result) {
|
||||||
|
return delegate().submit(Context.current().wrap(task), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Future<?> submit(Runnable task) {
|
||||||
|
return delegate().submit(Context.current().wrap(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
|
||||||
|
throws InterruptedException {
|
||||||
|
return delegate().invokeAll(wrap(Context.current(), tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> List<Future<T>> invokeAll(
|
||||||
|
Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||||
|
throws InterruptedException {
|
||||||
|
return delegate().invokeAll(wrap(Context.current(), tasks), timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
|
||||||
|
throws InterruptedException, ExecutionException {
|
||||||
|
return delegate().invokeAny(wrap(Context.current(), tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||||
|
throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
return delegate().invokeAny(wrap(Context.current(), tasks), timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Runnable command) {
|
||||||
|
delegate().execute(Context.current().wrap(command));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.context;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/** A {@link ExecutorService} that implements methods that don't need {@link Context}. */
|
||||||
|
abstract class ForwardingExecutorService implements ExecutorService {
|
||||||
|
|
||||||
|
private final ExecutorService delegate;
|
||||||
|
|
||||||
|
protected ForwardingExecutorService(ExecutorService delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorService delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void shutdown() {
|
||||||
|
delegate.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final List<Runnable> shutdownNow() {
|
||||||
|
return delegate.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isShutdown() {
|
||||||
|
return delegate.isShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isTerminated() {
|
||||||
|
return delegate.isTerminated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||||
|
return delegate.awaitTermination(timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <T> Collection<? extends Callable<T>> wrap(
|
||||||
|
Context context, Collection<? extends Callable<T>> tasks) {
|
||||||
|
List<Callable<T>> wrapped = new ArrayList<>();
|
||||||
|
for (Callable<T> task : tasks) {
|
||||||
|
wrapped.add(context.wrap(task));
|
||||||
|
}
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -217,6 +217,11 @@ class ContextTest {
|
||||||
|
|
||||||
executor.execute(callback);
|
executor.execute(callback);
|
||||||
assertThat(value).hasValue(null);
|
assertThat(value).hasValue(null);
|
||||||
|
|
||||||
|
try (Scope ignored = CAT.makeCurrent()) {
|
||||||
|
Context.taskWrapping(executor).execute(callback);
|
||||||
|
assertThat(value).hasValue("cat");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|
@ -227,10 +232,14 @@ class ContextTest {
|
||||||
protected ExecutorService wrapped;
|
protected ExecutorService wrapped;
|
||||||
protected AtomicReference<String> value;
|
protected AtomicReference<String> value;
|
||||||
|
|
||||||
|
protected ExecutorService wrap(ExecutorService executorService) {
|
||||||
|
return CAT.wrap(executorService);
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
void initExecutor() {
|
void initExecutor() {
|
||||||
executor = Executors.newSingleThreadScheduledExecutor();
|
executor = Executors.newSingleThreadScheduledExecutor();
|
||||||
wrapped = CAT.wrap((ExecutorService) executor);
|
wrapped = wrap(executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterAll
|
@AfterAll
|
||||||
|
|
@ -358,6 +367,30 @@ class ContextTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestInstance(Lifecycle.PER_CLASS)
|
||||||
|
class CurrentContextWrappingExecutorService extends WrapExecutorService {
|
||||||
|
@Override
|
||||||
|
protected ExecutorService wrap(ExecutorService executorService) {
|
||||||
|
return Context.taskWrapping(executorService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Scope scope;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
// Closed in AfterEach
|
||||||
|
@SuppressWarnings("MustBeClosedChecker")
|
||||||
|
void makeCurrent() {
|
||||||
|
scope = CAT.makeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void close() {
|
||||||
|
scope.close();
|
||||||
|
scope = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void keyToString() {
|
void keyToString() {
|
||||||
assertThat(ANIMAL.toString()).isEqualTo("animal");
|
assertThat(ANIMAL.toString()).isEqualTo("animal");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue