package org.dcache.util;
import com.google.common.util.concurrent.ForwardingListenableFuture;
import com.google.common.util.concurrent.ForwardingListeningExecutorService;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import dmg.cells.nucleus.CDC;
import static java.util.stream.Collectors.toList;
/**
* Decorates a ListeningExecutorService and makes tasks and futures CDC aware.
*
* The CDC of a task submitted to the ExecutorService will be initialized to
* the CDC of the thread that submitted the task. The CDC of any listeners
* added to a ListenableFuture returned by the decorator will be initialized
* to the CDC of thread that added the listener.
*/
public class CDCListeningExecutorServiceDecorator extends ForwardingListeningExecutorService
{
private final ListeningExecutorService _delegate;
public CDCListeningExecutorServiceDecorator(
ListeningExecutorService delegate)
{
this._delegate = delegate;
}
public CDCListeningExecutorServiceDecorator(
ExecutorService delegate)
{
this(MoreExecutors.listeningDecorator(delegate));
}
@Override
protected ListeningExecutorService delegate()
{
return _delegate;
}
@Override
public <T> ListenableFuture<T> submit(Callable<T> task)
{
return wrap(_delegate.submit(wrap(task)));
}
@Override
public ListenableFuture<?> submit(Runnable task)
{
return wrap(_delegate.submit(wrap(task)));
}
@Override
public <T> ListenableFuture<T> submit(Runnable task, T result)
{
return wrap(_delegate.submit(wrap(task), result));
}
@Override
public <T> List<Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks) throws InterruptedException
{
return wrap(_delegate.invokeAll(this.<T>wrap(tasks)));
}
@Override
public <T> List<Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks, long timeout,
TimeUnit unit) throws InterruptedException
{
return wrap(_delegate.invokeAll(this.<T>wrap(tasks), timeout, unit));
}
@Override
public <T> T invokeAny(
Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException
{
return _delegate.invokeAny(wrap(tasks));
}
@Override
public <T> T invokeAny(
Collection<? extends Callable<T>> tasks, long timeout,
TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
{
return _delegate.invokeAny(wrap(tasks), timeout, unit);
}
@Override
public void execute(Runnable command)
{
_delegate.execute(wrap(command));
}
private <T> ListenableFuture<T> wrap(final ListenableFuture<T> future)
{
return new ForwardingListenableFuture<T>()
{
@Override
public void addListener(Runnable listener, Executor exec)
{
super.addListener(wrap(listener), exec);
}
@Override
protected ListenableFuture<T> delegate()
{
return future;
}
};
}
private Runnable wrap(final Runnable task)
{
final CDC cdc = new CDC();
return () -> {
try (CDC ignored = cdc.restore()) {
task.run();
}
};
}
private <T> Callable<T> wrap(final Callable<T> task)
{
final CDC cdc = new CDC();
return () -> {
try (CDC ignored = cdc.restore()) {
return task.call();
}
};
}
private <T> Collection<? extends Callable<T>> wrap(Collection<? extends Callable<T>> tasks)
{
return tasks.stream().map(this::wrap).collect(toList());
}
private <T> List<Future<T>> wrap(List<Future<T>> futures)
{
return futures.stream().map(future -> wrap((ListenableFuture<T>) future)).collect(toList());
}
}