Java tutorial
/** * Copyright 2015 Leonid Bogdanov * * 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.mirantis.opendaylight; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import java.util.concurrent.Executor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.util.concurrent.*; /** * @author Leonid Bogdanov */ public class Retrier<V> { private static final Logger log = LoggerFactory.getLogger(Retrier.class); private static final int DEFAULT_RETRY_COUNT = 5; private volatile Supplier<ListenableFuture<V>> block; private volatile Predicate<Throwable> condition = Predicates.alwaysTrue(); private volatile Executor executor = MoreExecutors.sameThreadExecutor(); private int attempts = DEFAULT_RETRY_COUNT; public static <V> Retrier<V> create() { return new Retrier<V>(); } public Retrier<V> code(Supplier<ListenableFuture<V>> block) { this.block = checkNotNull(block, "Block arg cannot be null"); return this; } public Retrier<V> when(Predicate<Throwable> condition) { this.condition = checkNotNull(condition, "Condition arg cannot be null"); return this; } public Retrier<V> using(Executor executor) { this.executor = checkNotNull(executor, "Executor arg cannot be null"); return this; } public Retrier<V> atMost(int attempts) { checkArgument(attempts > 0, "Attempts arg must be greater than 0"); this.attempts = attempts; return this; } public ListenableFuture<V> retry() { SettableFuture<V> promise = SettableFuture.create(); int attempt = attempts; executor.execute(() -> doRetry(promise, attempt)); return promise; } private void doRetry(SettableFuture<V> promise, int attempt) { if (attempt > 0) { try { Futures.addCallback(block.get(), new FutureCallback<V>() { @Override public void onSuccess(V value) { promise.set(value); } @Override public void onFailure(Throwable t) { if (condition.apply(t)) { log.debug("Retrying", t); doRetry(promise, attempt - 1); } else { log.debug(t.getMessage(), t); promise.setException(t); } } }, executor); } catch (RuntimeException e) { log.debug("Couldn't get code block to retrying", e); promise.setException(e); } } else { promise.setException(new RuntimeException("Number of attempts exeeded")); } } }