/* * Copyright (c) 2008-2017, Hazelcast, Inc. 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. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.util.executor; import com.hazelcast.logging.ILogger; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.Delayed; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionHandler; 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 java.util.logging.Level; import static com.hazelcast.util.Preconditions.checkNotNull; import static java.lang.Thread.currentThread; import static java.util.logging.Level.FINE; import static java.util.logging.Level.SEVERE; /** * Logs execution exceptions by overriding {@link ScheduledThreadPoolExecutor#afterExecute} * and {@link ScheduledThreadPoolExecutor#decorateTask} methods. * * Reasoning is given tasks to {@link ScheduledThreadPoolExecutor} stops silently if there is an execution exception. * * Note: Task decoration is only needed to call given tasks {@code toString} methods. * * {@link java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay} */ public class LoggingScheduledExecutor extends ScheduledThreadPoolExecutor { private final ILogger logger; public LoggingScheduledExecutor(ILogger logger, int corePoolSize, ThreadFactory threadFactory) { super(corePoolSize, threadFactory); this.logger = checkNotNull(logger, "logger cannot be null"); } public LoggingScheduledExecutor(ILogger logger, int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, threadFactory, handler); this.logger = checkNotNull(logger, "logger cannot be null"); } @Override protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) { return new LoggingDelegatingFuture<V>(runnable, task); } @Override protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) { return new LoggingDelegatingFuture<V>(callable, task); } @Override protected void afterExecute(Runnable runnable, Throwable throwable) { super.afterExecute(runnable, throwable); Level level = FINE; if (throwable == null && runnable instanceof ScheduledFuture && ((ScheduledFuture) runnable).isDone()) { try { ((Future) runnable).get(); } catch (CancellationException ce) { throwable = ce; } catch (ExecutionException ee) { level = SEVERE; throwable = ee.getCause(); } catch (InterruptedException ie) { throwable = ie; currentThread().interrupt(); } } if (throwable != null) { logger.log(level, "Failed to execute " + runnable, throwable); } } /** * Only goal of this wrapping is to call given tasks {@code toString} method. Because * there is no straightforward way to reach it due to the internal wrapping done by * {@link ScheduledThreadPoolExecutor} * * {@link ScheduledThreadPoolExecutor#scheduleWithFixedDelay} */ static class LoggingDelegatingFuture<V> implements RunnableScheduledFuture<V> { private final Object task; private final RunnableScheduledFuture<V> delegate; LoggingDelegatingFuture(Object task, RunnableScheduledFuture<V> delegate) { this.task = task; this.delegate = delegate; } @Override public boolean isPeriodic() { return delegate.isPeriodic(); } @Override public long getDelay(TimeUnit unit) { return delegate.getDelay(unit); } @Override public void run() { delegate.run(); } @Override public int compareTo(Delayed o) { return delegate.compareTo(o); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof LoggingDelegatingFuture)) { return false; } LoggingDelegatingFuture<?> that = (LoggingDelegatingFuture<?>) o; return delegate.equals(that.delegate); } @Override public int hashCode() { return delegate.hashCode(); } @Override public boolean cancel(boolean mayInterruptIfRunning) { return delegate.cancel(mayInterruptIfRunning); } @Override public boolean isCancelled() { return delegate.isCancelled(); } @Override public boolean isDone() { return delegate.isDone(); } @Override public V get() throws InterruptedException, ExecutionException { return delegate.get(); } @Override public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return delegate.get(timeout, unit); } @Override public String toString() { return "LoggingDelegatingFuture{task=" + task + '}'; } } }