Java tutorial
/* * Copyright (C) 2011 Google Inc. * * 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 org.ros.concurrent; import com.google.common.collect.Maps; import org.apache.commons.logging.Log; import org.ros.exception.RosRuntimeException; import org.ros.log.RosLogFactory; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * Wraps an {@link ScheduledExecutorService} to execute {@link Callable}s with * retries. * * @author damonkohler@google.com (Damon Kohler) */ public class RetryingExecutorService { private static final Log log = RosLogFactory.getLog(RetryingExecutorService.class); private static final long DEFAULT_RETRY_DELAY = 5; private static final TimeUnit DEFAULT_RETRY_TIME_UNIT = TimeUnit.SECONDS; private final ScheduledExecutorService scheduledExecutorService; private final RetryLoop retryLoop; private final Map<Callable<Boolean>, CountDownLatch> latches; private final Map<Future<Boolean>, Callable<Boolean>> callables; private final CompletionService<Boolean> completionService; private final Object mutex; private long retryDelay; private TimeUnit retryTimeUnit; private boolean running; private class RetryLoop extends CancellableLoop { @Override public void loop() throws InterruptedException { Future<Boolean> future = completionService.take(); final Callable<Boolean> callable = callables.remove(future); boolean retry; try { retry = future.get(); } catch (ExecutionException e) { throw new RosRuntimeException(e.getCause()); } if (retry) { log.debug("Retry requested in RetryingExecutorService."); scheduledExecutorService.schedule(new Runnable() { @Override public void run() { submit(callable); } }, retryDelay, retryTimeUnit); } else { latches.get(callable).countDown(); } } } /** * @param scheduledExecutorService * the {@link ExecutorService} to wrap */ public RetryingExecutorService(ScheduledExecutorService scheduledExecutorService) { this.scheduledExecutorService = scheduledExecutorService; retryLoop = new RetryLoop(); latches = Maps.newConcurrentMap(); callables = Maps.newConcurrentMap(); completionService = new ExecutorCompletionService<Boolean>(scheduledExecutorService); mutex = new Object(); retryDelay = DEFAULT_RETRY_DELAY; retryTimeUnit = DEFAULT_RETRY_TIME_UNIT; running = true; // TODO(damonkohler): Unify this with the passed in ExecutorService. scheduledExecutorService.execute(retryLoop); } /** * Submit a new {@link Callable} to be executed. The submitted * {@link Callable} should return {@code true} to be retried, {@code false} * otherwise. * * @param callable * the {@link Callable} to execute * @throws RejectedExecutionException * if the {@link RetryingExecutorService} is shutting down */ public void submit(Callable<Boolean> callable) { synchronized (mutex) { if (running) { Future<Boolean> future = completionService.submit(callable); latches.put(callable, new CountDownLatch(1)); callables.put(future, callable); } else { throw new RejectedExecutionException(); } } } /** * @param delay * the delay in units of {@code unit} * @param unit * the {@link TimeUnit} of the delay */ public void setRetryDelay(long delay, TimeUnit unit) { retryDelay = delay; retryTimeUnit = unit; } /** * Stops accepting new {@link Callable}s and waits for all submitted * {@link Callable}s to finish within the specified timeout. * * @param timeout * the timeout in units of {@code unit} * @param unit * the {@link TimeUnit} of {@code timeout} * @throws InterruptedException */ public void shutdown(long timeout, TimeUnit unit) throws InterruptedException { running = false; for (CountDownLatch latch : latches.values()) { latch.await(timeout, unit); } retryLoop.cancel(); } }