001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.named.support; 020 021import java.util.concurrent.Callable; 022import java.util.concurrent.TimeUnit; 023import java.util.function.Predicate; 024 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028/** 029 * Retry helper: retries given {@code Callable} as long as it returns {@code null} (interpreted 030 * as "no answer yet") or given time passes. This helper implements similar semantics regarding 031 * caller threads as {@link java.util.concurrent.locks.Lock#tryLock(long, TimeUnit)} method does: 032 * blocks the caller thread until operation return non-{@code null} value within the given waiting 033 * time and the current thread has not been {@linkplain Thread#interrupt interrupted}. 034 * 035 * @since 1.7.3 036 */ 037public final class Retry { 038 private static final Logger LOGGER = LoggerFactory.getLogger(Retry.class); 039 040 private Retry() { 041 // no instances 042 } 043 044 /** 045 * Retries for given amount of time (time, unit) the passed in operation, sleeping given 046 * {@code sleepMills} between retries. In case operation returns {@code null}, it is assumed 047 * "is not done yet" state, so retry will happen (if time barrier allows). If time barrier 048 * passes, and still {@code null} ("is not done yet") is returned from operation, the 049 * {@code defaultResult} is returned. 050 */ 051 public static <R> R retry( 052 final long time, 053 final TimeUnit unit, 054 final long sleepMillis, 055 final Callable<R> operation, 056 final Predicate<Exception> retryPredicate, 057 final R defaultResult) 058 throws InterruptedException { 059 long now = System.nanoTime(); 060 final long barrier = now + unit.toNanos(time); 061 int attempt = 1; 062 R result = null; 063 while (now < barrier && result == null) { 064 try { 065 result = operation.call(); 066 if (result == null) { 067 LOGGER.trace("Retry attempt {}: no result", attempt); 068 Thread.sleep(sleepMillis); 069 } 070 } catch (InterruptedException e) { 071 throw e; 072 } catch (Exception e) { 073 LOGGER.trace("Retry attempt {}: operation failure", attempt, e); 074 if (retryPredicate != null && !retryPredicate.test(e)) { 075 throw new IllegalStateException(e); 076 } 077 } 078 now = System.nanoTime(); 079 attempt++; 080 } 081 return result == null ? defaultResult : result; 082 } 083 084 /** 085 * Retries attempting max given times the passed in operation, sleeping given 086 * {@code sleepMills} between retries. In case operation returns {@code null}, it is assumed 087 * "is not done yet" state, so retry will happen (if attempt count allows). If all attempts 088 * used, and still {@code null} ("is not done yet") is returned from operation, the 089 * {@code defaultResult} is returned. 090 * <p> 091 * Just to clear things up: 5 attempts is really 4 retries (once do it and retry 4 times). 0 attempts means 092 * "do not even try it", and this method returns without doing anything. 093 */ 094 public static <R> R retry( 095 final int attempts, 096 final long sleepMillis, 097 final Callable<R> operation, 098 final Predicate<Exception> retryPredicate, 099 final R defaultResult) 100 throws InterruptedException { 101 int attempt = 1; 102 R result = null; 103 while (attempt <= attempts && result == null) { 104 try { 105 result = operation.call(); 106 if (result == null) { 107 LOGGER.trace("Retry attempt {}: no result", attempt); 108 Thread.sleep(sleepMillis); 109 } 110 } catch (InterruptedException e) { 111 throw e; 112 } catch (Exception e) { 113 LOGGER.trace("Retry attempt {}: operation failure", attempt, e); 114 if (retryPredicate != null && !retryPredicate.test(e)) { 115 throw new IllegalStateException(e); 116 } 117 } 118 attempt++; 119 } 120 return result == null ? defaultResult : result; 121 } 122}