openrewrite / openrewrite/rewrite-spring

Spring Boot 4 - Migrate RetryTemplate from Spring Retry to Spring Resilience

Open
#1,024 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
403
Forks
149
Avg merge
2h 33m
Merged PRs (30d)
10

Description

What problem are you trying to solve?

Spring Boot 4 now has RetryTemplate as part of the core under the name "Spring Resilience". It would be nice to have an automated migration that can migrate from the RetryTemplate of the external spring-retry dependency to the RetryTemplate of Spring Resilience.

What precondition(s) should be checked before applying this recipe?

The project should be Spring Boot 4.

Describe the situation before applying the recipe

The RetryTemplate can be created via the constructor:

RetryTemplate retryTemplate = new RetryTemplate();
        TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
        policy.setTimeout(30000L);

        retryTemplate.setRetryPolicy(policy);

        retryTemplate.execute(new RetryCallback<Object, Throwable>() {
            @Override
            public Object doWithRetry(RetryContext context) throws Throwable {
                LOGGER.info("Doing something...");
                LOGGER.info("Context: {}", context);
                return null;
            }
        });

Or it can be created via the builder:

 RetryTemplate retryTemplate = RetryTemplate.builder()
                .retryOn(IllegalArgumentException.class)
                .withTimeout(Duration.ofSeconds(30))
                .build();

        retryTemplate.execute((RetryCallback<Object, Throwable>) context -> {
            LOGGER.info("Doing something...");
            LOGGER.info("Context: {}", context);
            return null;
        });

The RetryTemplate can also be created separately as a @Bean in some @Configuration class and injected where it is used:

@Configuration
public class AppConfig {

    private FixedBackOffPolicy fixedBackOffPolicy(long backOffPeriod) {
        FixedBackOffPolicy policy = new FixedBackOffPolicy();
        policy.setBackOffPeriod(backOffPeriod);
        return policy;
    }

    @Bean
    public RetryTemplate retryTemplate() {
        return RetryTemplate.builder()
          .maxAttempts(3)
          .customBackoff(fixedBackOffPolicy(2000L))
          .withListener(new DefaultListenerSupport())
          .build();
    }

    @Bean
    public RetryTemplate retryTemplateNoRetry() {
        return RetryTemplate.builder()
          .maxAttempts(1)
          .customBackoff(fixedBackOffPolicy(100L))
          .build();
    }
}

(Example taken from https://www.baeldung.com/spring-retry )

Describe the situation after applying the recipe

After applying the recipe, the dependency should be removed:

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

For the first example this could be the result

RetryTemplate retryTemplate = new RetryTemplate(RetryPolicy.builder()
                .timeout(Duration.ofSeconds(30))
                .build());

        retryTemplate.execute(new Retryable<Object>() {
            @Override
            public Object execute() throws Throwable {
                LOGGER.info("Doing something...");
                // TODO - context no longer available - LOGGER.info("Context: {}", context);
                return null;
            }
        });```

For the second example:

```java
RetryTemplate retryTemplate = new RetryTemplate(RetryPolicy.builder()
                .includes(IllegalArgumentException.class)
                .timeout(Duration.ofSeconds(30))
                .build());

Note that the imports changed from org.springframework.retry to org.springframework.core.retry

Have you considered any alternatives or workarounds?

Any additional context

  1. The context object is no longer available in the lambda, so that is why I think adding a TODO is probably the best you can do automatically.
  2. There is an execute and an invoke method. Not sure which one will be easiest to migrate to. The documentation at https://docs.spring.io/spring-framework/reference/core/resilience.html#resilience-programmatic-retry seems to prefer invoke, so maybe the migration should prefer that as well.

Are you interested in contributing this recipe to OpenRewrite?

I think this is a bit over my head, and I don't have the bandwidth currently to work on this.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start from the issue's Spring Retry constructor, builder, bean, and callback examples, then compare them with the linked Spring Framework programmatic resilience documentation. Define the supported Spring Boot 4 preconditions and migration behavior for dependencies, imports, policies, callbacks, and execute versus invoke. Done means the described examples migrate consistently, including the context limitation, with coverage for each supported form.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.