public void perform(String url, FetchHandler handler) throws InterruptedException {
        int retryCount = 0;
        while (true) {
            retryCount++;
            String message = "";
            try {
                int rc = download(httpService, url, handler);
                if (handler.handleResult(rc, goPublisher)) {
                    return;
                }
                message = String.format("Unsuccessful response '%s' from the server", rc);
            } catch (Exception e) {
                message = String.format("Caught an exception '%s'", e.getMessage());
            }
            if (retryCount > 3) {
                message = String.format("Giving up fetching resource '%s'. Tried 4 times and failed.", url);
                LOG.error(message);
                throw new RuntimeException(message);
            }
            long backout = Math.round(backout(retryCount));
            publishDownloadError(url, message, backout);
            clock.sleepForSeconds(backout);
        }
    }
@Test
    public void shouldRetryThreeTimesWhenDownloadFails() throws Exception {
        when(fetchHandler.handleResult(200, publisher)).thenReturn(true);
        try (LogFixture logging = logFixtureFor(DownloadAction.class, Level.DEBUG)) {
            FailSometimesHttpService httpService = new FailSometimesHttpService(3);
            DownloadAction downloadAction = new DownloadAction(httpService, publisher, clock);
            downloadAction.perform("foo", fetchHandler);

            assertThat(httpService.timesCalled, is(4));

            shouldHaveLogged(logging, Level.WARN, "Could not fetch artifact foo.");
            shouldHaveLogged(logging, Level.WARN, "Error was : Caught an exception 'Connection Reset'");

            assertBetween(clock.getSleeps().get(0), 10000L, 20000L);
            assertBetween(clock.getSleeps().get(1), 20000L, 30000L);
            assertBetween(clock.getSleeps().get(2), 30000L, 40000L);
            assertThat(clock.getSleeps().size(), is(3));
        }
    }