/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.service; import java.util.ArrayList; 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; import com.opengamma.util.ArgumentChecker; /** * Wraps another {@link ExecutorService} instance, ensuring {@link ThreadLocalServiceContext} is initialized before * every task is run and that it is cleared after every task completes. {@link ThreadLocalServiceContext#init} is * called on the pooled thread using the {@link ServiceContext} returned by * {@link ThreadLocalServiceContext#getInstance()} on the thread that submits the task. */ public class ServiceContextAwareExecutorService implements ExecutorService { /** Used to run the tasks. */ private final ExecutorService _delegateExecutor; /** * @param delegate the underlying {@link ExecutorService} used to execute tasks */ public ServiceContextAwareExecutorService(ExecutorService delegate) { _delegateExecutor = ArgumentChecker.notNull(delegate, "delegate"); } @Override public void shutdown() { _delegateExecutor.shutdown(); } @Override public List<Runnable> shutdownNow() { return _delegateExecutor.shutdownNow(); } @Override public boolean isShutdown() { return _delegateExecutor.isShutdown(); } @Override public boolean isTerminated() { return _delegateExecutor.isTerminated(); } @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return _delegateExecutor.awaitTermination(timeout, unit); } @Override public <T> Future<T> submit(Callable<T> task) { return _delegateExecutor.submit(new ServiceContextAwareCallable<>(task)); } @Override public <T> Future<T> submit(Runnable task, T result) { return _delegateExecutor.submit(new ServiceContextAwareRunnable(task), result); } @Override public Future<?> submit(Runnable task) { return _delegateExecutor.submit(new ServiceContextAwareRunnable(task)); } @Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { return _delegateExecutor.invokeAll(wrapTasks(tasks)); } @Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { return _delegateExecutor.invokeAll(wrapTasks(tasks), timeout, unit); } @Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return _delegateExecutor.invokeAny(wrapTasks(tasks)); } @Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return _delegateExecutor.invokeAny(wrapTasks(tasks), timeout, unit); } @Override public void execute(Runnable command) { _delegateExecutor.execute(new ServiceContextAwareRunnable(command)); } /** * @return the tasks wrapped in instances of {@link ServiceContextAwareCallable}. */ private <T> List<Callable<T>> wrapTasks(Collection<? extends Callable<T>> tasks) { List<Callable<T>> taskList = new ArrayList<>(tasks.size()); for (Callable<T> task : tasks) { taskList.add(new ServiceContextAwareCallable<>(task)); } return taskList; } /** * {@link Callable} implementation that sets up and tears down thread local {@link ServiceContext} bindings around * a call to a delegate {@link Callable} instance. */ private final class ServiceContextAwareCallable<T> implements Callable<T> { private final Callable<T> _delegateCallable; /** The service context used to initialize {@link ThreadLocalServiceContext} before task execution. */ private final ServiceContext _serviceContext; private ServiceContextAwareCallable(Callable<T> delegateCallable) { _delegateCallable = delegateCallable; _serviceContext = ThreadLocalServiceContext.getInstance(); } @Override public T call() throws Exception { try { ThreadLocalServiceContext.init(_serviceContext); return _delegateCallable.call(); } finally { ThreadLocalServiceContext.init(null); } } } /** * {@link Runnable} implementation that sets up and tears down thread local {@link ServiceContext} bindings around * a call to a delegate {@link Runnable} instance. */ private final class ServiceContextAwareRunnable implements Runnable { private final Runnable _delegateRunnable; /** The service context used to initialize {@link ThreadLocalServiceContext} before task execution. */ private final ServiceContext _serviceContext; private ServiceContextAwareRunnable(Runnable delegateRunnable) { _delegateRunnable = delegateRunnable; _serviceContext = ThreadLocalServiceContext.getInstance(); } @Override public void run() { try { ThreadLocalServiceContext.init(_serviceContext); _delegateRunnable.run(); } finally { ThreadLocalServiceContext.init(null); } } } }