ResilientOperation { @SuppressWarnings("unchecked") public static <T, X extends Exception> T retry( CheckedCallable<T, X> callable, BackOff backoff, RetryDeterminer<? super X> retryDet, Class<X> classType, Sleeper sleeper) throws X, InterruptedException { checkNotNull(backoff, "Must provide a non-null BackOff."); checkNotNull(retryDet, "Must provide a non-null RetryDeterminer."); checkNotNull(sleeper, "Must provide a non-null Sleeper."); checkNotNull(callable, "Must provide a non-null Executable object."); X currentException; do { try { return callable.call(); } catch (Exception e) { if (classType.isInstance(e)) { currentException = (X) e; if (!retryDet.shouldRetry(currentException)) { throw currentException; } } else { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException( "Retrying with unchecked exceptions that are not RuntimeExceptions is not supported.", e); } } } while (nextSleep(backoff, sleeper, currentException)); throw currentException; }  @SuppressWarnings("unchecked") static T retry(
      CheckedCallable<T, X> callable,
      BackOff backoff,
      RetryDeterminer<? super X> retryDet,
      Class<X> classType,
      Sleeper sleeper); static T retry(
      CheckedCallable<T, X> callable,
      BackOff backoff,
      RetryDeterminer<? super X> retryDet,
      Class<X> classType); static CheckedCallable<V, IOException> getGoogleRequestCallable(
      AbstractGoogleClientRequest<V> request);  }
@Test public void testValidCallHasNoRetries() throws Exception { MockSleeper sleeper = new MockSleeper(); CallableTester<Exception> callTester = new CallableTester<>(new ArrayList<Exception>()); BackOff backoff = new RetryBoundedBackOff(3, new BackOffTester()); ResilientOperation.retry(callTester, backoff, RetryDeterminer.DEFAULT, Exception.class, sleeper); assertThat(callTester.timesCalled()).isEqualTo(1); assertThat(sleeper.getCount()).isEqualTo(0); }
@Test public void testCallFailsOnBadException() throws Exception { MockSleeper sleeper = new MockSleeper(); ArrayList<Exception> exceptions = new ArrayList<>(); exceptions.add(new IllegalArgumentException("FakeException")); CallableTester<Exception> callTester = new CallableTester<>(exceptions); BackOff backoff = new RetryBoundedBackOff(3, new BackOffTester()); IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, () -> ResilientOperation.retry( callTester, backoff, RetryDeterminer.DEFAULT, Exception.class, sleeper)); assertThat(thrown).hasMessageThat().contains("FakeException"); assertThat(callTester.timesCalled()).isEqualTo(1); verifySleeper(sleeper, 0); }
@Test public void testCallRetriesAndFails() throws Exception { MockSleeper sleeper = new MockSleeper(); ArrayList<Exception> exceptions = new ArrayList<>(); exceptions.add(new SocketTimeoutException("socket")); exceptions.add(new SocketTimeoutException("socket")); exceptions.add(new IllegalArgumentException("FakeException")); CallableTester<Exception> callTester = new CallableTester<>(exceptions); BackOff backoff = new RetryBoundedBackOff(5, new BackOffTester()); IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, () -> ResilientOperation.retry( callTester, backoff, RetryDeterminer.DEFAULT, Exception.class, sleeper)); assertThat(thrown).hasMessageThat().contains("FakeException"); assertThat(callTester.timesCalled()).isEqualTo(3); verifySleeper(sleeper, 2); }
@Test public void testCallRetriesAndFailsWithSocketErrors() throws Exception { MockSleeper sleeper = new MockSleeper(); ArrayList<IOException> exceptions = new ArrayList<>(); exceptions.add(new SocketTimeoutException("socket")); exceptions.add(new SocketTimeoutException("socket")); exceptions.add(new IOException("FakeException")); CallableTester<IOException> callTester = new CallableTester<>(exceptions); BackOff backoff = new RetryBoundedBackOff(5, new BackOffTester()); IOException thrown = assertThrows( IOException.class, () -> ResilientOperation.retry( callTester, backoff, RetryDeterminer.SOCKET_ERRORS, IOException.class, sleeper)); assertThat(thrown).hasMessageThat().contains("FakeException"); assertThat(callTester.timesCalled()).isEqualTo(3); verifySleeper(sleeper, 2); }
@Test public void testCallMaxRetries() throws Exception { MockSleeper sleeper = new MockSleeper(); ArrayList<Exception> exceptions = new ArrayList<>(); exceptions.add(new SocketTimeoutException("socket")); exceptions.add(new SocketTimeoutException("socket2")); exceptions.add(new SocketTimeoutException("socket3")); CallableTester<Exception> callTester = new CallableTester<>(exceptions); BackOff backoff = new RetryBoundedBackOff(2, new BackOffTester()); SocketTimeoutException thrown = assertThrows( SocketTimeoutException.class, () -> ResilientOperation.retry( callTester, backoff, RetryDeterminer.DEFAULT, Exception.class, sleeper)); assertThat(thrown).hasMessageThat().contains("socket3"); assertThat(callTester.timesCalled()).isEqualTo(3); verifySleeper(sleeper, 2); }
@Test public void testCallRetriesAndSucceeds() throws Exception { MockSleeper sleeper = new MockSleeper(); ArrayList<Exception> exceptions = new ArrayList<>(); exceptions.add(new SocketTimeoutException("socket")); exceptions.add(new SocketTimeoutException("socket2")); exceptions.add(new SocketTimeoutException("socket3")); CallableTester<Exception> callTester = new CallableTester<>(exceptions); BackOff backoff = new RetryBoundedBackOff(3, new BackOffTester()); assertThat( ResilientOperation.retry( callTester, backoff, RetryDeterminer.DEFAULT, Exception.class, sleeper)) .isEqualTo(3); assertThat(callTester.timesCalled()).isEqualTo(4); verifySleeper(sleeper, 3); }