/* * Copyright (C) 2014-2017 LinkedIn Corp. All rights reserved. * * 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 http://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. */ package gobblin.util.executors; import com.google.common.base.Throwables; import com.google.common.util.concurrent.AbstractFuture; 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.ListenableFutureTask; import com.google.common.util.concurrent.ListenableScheduledFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.concurrent.Callable; import java.util.concurrent.Delayed; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; public class MDCPropagatingScheduledExecutorService extends ForwardingListeningExecutorService implements ListeningScheduledExecutorService { private final ListeningScheduledExecutorService executorService; public MDCPropagatingScheduledExecutorService(ScheduledExecutorService executorService) { if (executorService instanceof ListeningScheduledExecutorService) { this.executorService = (ListeningScheduledExecutorService)executorService; } else { this.executorService = MoreExecutors.listeningDecorator(executorService); } } @Override protected ListeningExecutorService delegate() { return executorService; } @Override public void execute(Runnable command) { super.execute(new MDCPropagatingRunnable(command)); } @Override public <T> ListenableFuture<T> submit(Callable<T> task) { return super.submit(new MDCPropagatingCallable<T>(task)); } @Override public ListenableFuture<?> submit(Runnable task) { return super.submit(new MDCPropagatingRunnable(task)); } @Override public <T> ListenableFuture<T> submit(Runnable task, T result) { return super.submit(new MDCPropagatingRunnable(task), result); } @Override public ListenableScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { ListenableFutureTask<Void> task = ListenableFutureTask.create(new MDCPropagatingRunnable(command), null); ScheduledFuture<?> scheduled = executorService.schedule(task, delay, unit); return new ListenableScheduledTask<>(task, scheduled); } @Override public <V> ListenableScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { ListenableFutureTask<V> task = ListenableFutureTask.create(new MDCPropagatingCallable<>(callable)); ScheduledFuture<?> scheduled = executorService.schedule(task, delay, unit); return new ListenableScheduledTask<>(task, scheduled); } @Override public ListenableScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { NeverSuccessfulListenableFutureTask task = new NeverSuccessfulListenableFutureTask(new MDCPropagatingRunnable(command)); ScheduledFuture<?> scheduled = executorService.scheduleAtFixedRate(task, initialDelay, period, unit); return new ListenableScheduledTask<>(task, scheduled); } @Override public ListenableScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { NeverSuccessfulListenableFutureTask task = new NeverSuccessfulListenableFutureTask(new MDCPropagatingRunnable(command)); ScheduledFuture<?> scheduled = executorService.scheduleWithFixedDelay(task, initialDelay, delay, unit); return new ListenableScheduledTask<>(task, scheduled); } @SuppressFBWarnings("EQ_COMPARETO_USE_OBJECT_EQUALS") private static final class ListenableScheduledTask<V> extends ForwardingListenableFuture.SimpleForwardingListenableFuture<V> implements ListenableScheduledFuture<V> { private final ScheduledFuture<?> scheduledDelegate; public ListenableScheduledTask(ListenableFuture<V> listenableDelegate, ScheduledFuture<?> scheduledDelegate) { super(listenableDelegate); this.scheduledDelegate = scheduledDelegate; } @Override public boolean cancel(boolean mayInterruptIfRunning) { boolean cancelled = super.cancel(mayInterruptIfRunning); if (cancelled) { scheduledDelegate.cancel(mayInterruptIfRunning); } return cancelled; } @Override public long getDelay(TimeUnit unit) { return scheduledDelegate.getDelay(unit); } @Override public int compareTo(Delayed other) { return scheduledDelegate.compareTo(other); } } private static final class NeverSuccessfulListenableFutureTask extends AbstractFuture<Void> implements Runnable { private final Runnable delegate; public NeverSuccessfulListenableFutureTask(Runnable delegate) { this.delegate = checkNotNull(delegate); } @Override public void run() { try { delegate.run(); } catch (Throwable t) { setException(t); throw Throwables.propagate(t); } } } }