Enterprise Java

Retrying RestTemplate Requests in Java When Hosts Are Offline

Spring’s RestTemplate is a popular client for making RESTful API calls. By default, it does not automatically retry failed requests. If the target host is offline or temporarily unavailable, exceptions like SocketTimeoutException or UnknownHostException are thrown, which can disrupt your application. In this tutorial, we will explore how to implement automatic retries for RestTemplate requests in a clean, reusable way using Spring Boot. This approach will enable your application to handle temporary network failures with greater ease.

1. Setting Up the Project

We begin by setting up a Spring Boot project with the necessary dependencies, and for this article, we use Maven, as shown in the pom.xml below.

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
            <version>2.0.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aspectj</artifactId>
        </dependency>

The spring-retry dependency enables the retry mechanisms used throughout the application, while spring-boot-starter-aspectj is required to support annotation-based retries. With these dependencies in place, we can proceed to configure both the RestTemplate and RetryTemplate beans.

2. Annotation-Based Retry Using @Retryable

Spring Retry allows methods to automatically retry failed operations using annotations. This approach is declarative and requires minimal code changes.

Configuration

@Configuration
@EnableRetry
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(Duration.ofSeconds(30));
        factory.setConnectTimeout(Duration.ofSeconds(30));

        return new RestTemplate(factory);
    }
}

This configuration class enables retry support through the @EnableRetry annotation, which activates Spring Retry’s capability to intercept and re-execute methods annotated with retry-related metadata. The class also defines a RestTemplate bean that is used for performing HTTP requests.

Instead of using the default RestTemplate settings, the configuration explicitly creates an SimpleClientHttpRequestFactory. This factory allows customising important timeout properties, such as the read timeout and the connection request timeout, both set to 30 seconds. These timeouts help prevent the application from hanging indefinitely when a host is offline or unresponsive, ensuring that retry logic is triggered promptly.

By returning a RestTemplate instance configured with this request factory, the application gains predictable HTTP behaviour, particularly under unstable network conditions.

Service Implementation

@Service
public class HttpService {

    private static final Logger logger = Logger.getLogger(HttpService.class.getName());

    private final RestTemplate restTemplate;

    public HttpService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Retryable(
            retryFor = {
                ResourceAccessException.class
            },
            maxAttempts = 5,
            backoff = @Backoff(delay = 2000)
    )
    public String fetchData(String url) {
        logger.log(Level.INFO, "Retry attempt to call URL {0}", url);
        return Objects.requireNonNull(restTemplate.getForObject(url, String.class));
    }

    @Recover
    public String recoverAfterRetries(ResourceAccessException ex, String url) {
        logger.log(Level.SEVERE, "All retry attempts failed for URL {0}. Triggering fallback. Error: {1}",
                new Object[]{url, ex.getMessage()});
        return "Fallback response after all retries failed for: " + url;
    }
}

In this implementation, the @Retryable annotation enables automatic retries when specific exceptions occur during the remote call. The retryFor attribute lists the exceptions that should trigger a retry, such as ResourceAccessException (typical for connection issues or timeouts). The maxAttempts value sets the total number of retry attempts to five, while the backoff configuration applies a fixed 2000 millisecond delay between each attempt. Together, these settings ensure the method is retried in a controlled and predictable way whenever a transient failure occurs.

The @Recover method works as a final fallback mechanism. After all retry attempts are exhausted, Spring Retry automatically invokes this method instead of rethrowing the exception.

Testing Annotation-Based Retry

@SpringBootApplication
public class ResttemplateRetryApplication implements CommandLineRunner {

    private static final Logger logger = Logger.getLogger(ResttemplateRetryApplication.class.getName());
    private final HttpService httpService;

    public ResttemplateRetryApplication(HttpService httpService) {
        this.httpService = httpService;
    }

    public static void main(String[] args) {
        SpringApplication.run(ResttemplateRetryApplication.class, args);
    }

    @Override
    public void run(String... args) {
        String testUrl = "http://localhost:9999/unreachable";

        try {
            httpService.fetchData(testUrl);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Retry failed after all attempts: {0}", e.getMessage());
        }
    }
}

When the application starts, it attempts to call an offline URL (http://localhost:9999/unreachable). The request fails immediately, triggering a ResourceAccessException. Because the method is annotated with @Retryable, Spring Retry captures the exception and retries the call according to the configured policy, applying the defined backoff delay between attempts.

Once all retry attempts are exhausted, Spring invokes the @Recover method, which logs a fallback message and returns a predefined response instead of propagating the exception.

Example Output

Example Output: Spring Retry Handling RestTemplate Requests to an Offline Host

3. Programmatic Retry Using RetryTemplate

For more flexibility and custom control, Spring Retry’s RetryTemplate offers a programmatic approach to handle retries.

Configuration

@Configuration
public class RetryTemplateConfig {

    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        // Define retry policy: max 5 attempts for all exceptions
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(5);

        retryTemplate.setRetryPolicy(retryPolicy);

        // Define backoff policy: wait 2 seconds between retries
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(2000); // 2 seconds delay

        retryTemplate.setBackOffPolicy(backOffPolicy);

        return retryTemplate;
    }
}

In this configuration, a SimpleRetryPolicy defines the maximum number of retry attempts, which is set to five for all exceptions. A FixedBackOffPolicy defines a fixed delay of two seconds between each retry attempt, ensuring that retries occur in a controlled and predictable manner.

Service Implementation

@Service
public class HttpServiceWithRetryTemplate {

    private static final Logger logger = Logger.getLogger(HttpServiceWithRetryTemplate.class.getName());
    private final RestTemplate restTemplate;
    private final RetryTemplate retryTemplate;

    public HttpServiceWithRetryTemplate(RestTemplate restTemplate, RetryTemplate retryTemplate) {
        this.restTemplate = restTemplate;
        this.retryTemplate = retryTemplate;
    }

    public String fetchDataWithRetry(String url) {
        return retryTemplate.execute(context -> {
            logger.log(Level.INFO, "Retry attempt #{0} to call URL: {1}", new Object[]{context.getRetryCount() + 1, url});
            return restTemplate.getForObject(url, String.class);
        });
    }
}

This class demonstrates a programmatic approach to retries using Spring Retry’s RetryTemplate in combination with RestTemplate for HTTP calls. The retryTemplate.execute() method wraps the code that should be retried.

Testing Programmatic Retry

@SpringBootApplication
public class ResttemplateRetryApplication implements CommandLineRunner {

    private static final Logger logger = Logger.getLogger(ResttemplateRetryApplication.class.getName());
    private final HttpServiceWithRetryTemplate httpService;

    public ResttemplateRetryApplication(HttpServiceWithRetryTemplate httpService) {
        this.httpService = httpService;
    }

    public static void main(String[] args) {
        SpringApplication.run(ResttemplateRetryApplication.class, args);
    }

    @Override
    public void run(String... args) {
        String testUrl = "http://localhost:9999/unreachable";

        try {
            httpService.fetchDataWithRetry(testUrl);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Retry failed after all attempts: {0}", e.getMessage());
        }
    }
}

This code snippet attempts to fetch data from an unreachable host, and the RetryTemplate automatically retries the request up to five times, applying a two-second delay between each attempt.

4. Conclusion

In this article, we explored how to implement automatic retries for RestTemplate HTTP requests in Spring Boot when the host is offline or temporarily unavailable. We demonstrated two approaches: annotation-based retries using @Retryable with @Recover for fallback handling, and a programmatic approach using RetryTemplate for more flexible control over retry behaviour.

Both approaches enhance application resilience against temporary network failures by ensuring requests are retried consistently and that fallback mechanisms are executed when needed. Using these techniques allows us to create more fault-tolerant REST clients in Java.

5. Download the Source Code

This article was on using Spring Retry to handle RestTemplate HTTP requests when the host is offline.

Download
You can download the full source code of this example here: spring retry RestTemplate HTTP requests host offline

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button