Skip to content

Commit bddc4ce

Browse files
committed
Adding Spring Retry implementation using custom retry policy
1 parent 57aa531 commit bddc4ce

13 files changed

+365
-20
lines changed

.gitignore

+33-20
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
1-
# Compiled class file
2-
*.class
1+
HELP.md
2+
target/
3+
!.mvn/wrapper/maven-wrapper.jar
4+
!**/src/main/**/target/
5+
!**/src/test/**/target/
36

4-
# Log file
5-
*.log
7+
### STS ###
8+
.apt_generated
9+
.classpath
10+
.factorypath
11+
.project
12+
.settings
13+
.springBeans
14+
.sts4-cache
615

7-
# BlueJ files
8-
*.ctxt
16+
### IntelliJ IDEA ###
17+
.idea
18+
*.iws
19+
*.iml
20+
*.ipr
921

10-
# Mobile Tools for Java (J2ME)
11-
.mtj.tmp/
22+
### NetBeans ###
23+
/nbproject/private/
24+
/nbbuild/
25+
/dist/
26+
/nbdist/
27+
/.nb-gradle/
28+
build/
29+
!**/src/main/**/build/
30+
!**/src/test/**/build/
1231

13-
# Package Files #
14-
*.jar
15-
*.war
16-
*.nar
17-
*.ear
18-
*.zip
19-
*.tar.gz
20-
*.rar
21-
22-
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23-
hs_err_pid*
24-
replay_pid*
32+
### VS Code ###
33+
.vscode/
34+
/.mvn/wrapper/maven-wrapper.jar
35+
/.mvn/wrapper/maven-wrapper.properties
36+
/mvnw
37+
/mvnw.cmd

pom.xml

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-starter-parent</artifactId>
8+
<version>3.0.6</version>
9+
<relativePath/> <!-- lookup parent from repository -->
10+
</parent>
11+
<groupId>com.bootcamptoprod</groupId>
12+
<artifactId>spring-boot-retry-with-custom-retry-policy</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
15+
<name>spring-boot-retry-with-custom-retry-policy</name>
16+
<description>A simple app highlighting how we can implement retry mechanism using custom retry policy in Spring Boot</description>
17+
18+
<properties>
19+
<java.version>17</java.version>
20+
</properties>
21+
22+
<dependencies>
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter-web</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.springframework.retry</groupId>
29+
<artifactId>spring-retry</artifactId>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.springframework</groupId>
33+
<artifactId>spring-aspects</artifactId>
34+
</dependency>
35+
36+
<dependency>
37+
<groupId>org.springframework.boot</groupId>
38+
<artifactId>spring-boot-starter-test</artifactId>
39+
<scope>test</scope>
40+
</dependency>
41+
</dependencies>
42+
43+
<build>
44+
<plugins>
45+
<plugin>
46+
<groupId>org.springframework.boot</groupId>
47+
<artifactId>spring-boot-maven-plugin</artifactId>
48+
</plugin>
49+
</plugins>
50+
</build>
51+
52+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.bootcamptoprod.retry;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.retry.annotation.EnableRetry;
6+
7+
@EnableRetry
8+
@SpringBootApplication
9+
public class SpringBootRetryApplication {
10+
11+
public static void main(String[] args) {
12+
SpringApplication.run(SpringBootRetryApplication.class, args);
13+
}
14+
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.bootcamptoprod.retry.config;
2+
3+
import org.springframework.boot.web.client.RestTemplateBuilder;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.web.client.RestTemplate;
7+
8+
@Configuration
9+
public class ApplicationConfig {
10+
11+
@Bean
12+
public RestTemplate restTemplate(RestTemplateBuilder builder) {
13+
return builder.build();
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.bootcamptoprod.retry.config;
2+
3+
import com.bootcamptoprod.retry.retry.CustomRetryPolicy;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.retry.backoff.FixedBackOffPolicy;
8+
import org.springframework.retry.support.RetryTemplate;
9+
10+
@Configuration
11+
public class RetryConfig {
12+
13+
@Value("${retry.maxAttempts:3}")
14+
private int maxAttempts;
15+
16+
@Value("${retry.backoffInMillis:2000}")
17+
private long backoffInMillis;
18+
19+
@Bean
20+
public RetryTemplate retryTemplate() {
21+
RetryTemplate retryTemplate = new RetryTemplate();
22+
23+
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
24+
backOffPolicy.setBackOffPeriod(backoffInMillis);
25+
retryTemplate.setBackOffPolicy(backOffPolicy);
26+
27+
CustomRetryPolicy retryPolicy = new CustomRetryPolicy(maxAttempts);
28+
retryTemplate.setRetryPolicy(retryPolicy);
29+
30+
return retryTemplate;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.bootcamptoprod.retry.controller;
2+
3+
import com.bootcamptoprod.retry.entity.Movie;
4+
import com.bootcamptoprod.retry.service.MovieService;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.PathVariable;
10+
import org.springframework.web.bind.annotation.RequestMapping;
11+
import org.springframework.web.bind.annotation.RestController;
12+
import org.springframework.web.client.ResourceAccessException;
13+
14+
@RestController
15+
@RequestMapping("/movies")
16+
public class MovieController {
17+
18+
@Autowired
19+
private MovieService movieService;
20+
21+
@GetMapping("/{id}")
22+
public ResponseEntity<Movie> getMovieById(@PathVariable String id) {
23+
try {
24+
Movie movie = movieService.getMovieDetails(id);
25+
return ResponseEntity.ok(movie);
26+
} catch (ResourceAccessException e) {
27+
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();
28+
}
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.bootcamptoprod.retry.controller.mock;
2+
3+
import com.bootcamptoprod.retry.entity.Movie;
4+
import com.bootcamptoprod.retry.exception.MovieNotFoundException;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.PathVariable;
7+
import org.springframework.web.bind.annotation.RequestMapping;
8+
import org.springframework.web.bind.annotation.RestController;
9+
10+
@RestController
11+
@RequestMapping("/mock/movies")
12+
public class MockMovieController {
13+
14+
@GetMapping("/{id}")
15+
public Movie getMovieById(@PathVariable String id) {
16+
if (id.equals("1")) {
17+
return new Movie("1", "The Matrix", "Lana Wachowski, Lilly Wachowski", 8.7);
18+
} else if (id.equals("2")) {
19+
return new Movie("2", "Inception", "Christopher Nolan", 8.8);
20+
} else {
21+
throw new MovieNotFoundException("Movie with id " + id + " not found.");
22+
}
23+
}
24+
}
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.bootcamptoprod.retry.entity;
2+
3+
public class Movie {
4+
private String id;
5+
private String title;
6+
private String director;
7+
private double rating;
8+
9+
public Movie(String id, String title, String director, double rating) {
10+
this.id = id;
11+
this.title = title;
12+
this.director = director;
13+
this.rating = rating;
14+
}
15+
16+
public String getId() {
17+
return id;
18+
}
19+
20+
public void setId(String id) {
21+
this.id = id;
22+
}
23+
24+
public String getTitle() {
25+
return title;
26+
}
27+
28+
public void setTitle(String title) {
29+
this.title = title;
30+
}
31+
32+
public String getDirector() {
33+
return director;
34+
}
35+
36+
public void setDirector(String director) {
37+
this.director = director;
38+
}
39+
40+
public double getRating() {
41+
return rating;
42+
}
43+
44+
public void setRating(double rating) {
45+
this.rating = rating;
46+
}
47+
}
48+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.bootcamptoprod.retry.exception;
2+
3+
public class MovieNotFoundException extends RuntimeException {
4+
public MovieNotFoundException(String message) {
5+
super(message);
6+
}
7+
}
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.bootcamptoprod.retry.rest.client;
2+
3+
import com.bootcamptoprod.retry.entity.Movie;
4+
import com.bootcamptoprod.retry.exception.MovieNotFoundException;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.client.RestTemplate;
8+
9+
@Component
10+
public class MovieApiClient {
11+
12+
@Autowired
13+
private RestTemplate restTemplate;
14+
15+
public Movie getMovieDetails(String movieId) {
16+
String url = "http://localhost:8080/mock/movies/" + movieId;
17+
Movie movie = restTemplate.getForObject(url, Movie.class);
18+
if (movie == null) {
19+
throw new MovieNotFoundException("Movie with id " + movieId + " not found.");
20+
}
21+
return movie;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.bootcamptoprod.retry.retry;
2+
3+
import org.springframework.retry.RetryContext;
4+
import org.springframework.retry.RetryPolicy;
5+
import org.springframework.retry.context.RetryContextSupport;
6+
7+
public class CustomRetryPolicy implements RetryPolicy {
8+
9+
private int maxAttempts;
10+
11+
public CustomRetryPolicy(int maxAttempts) {
12+
this.maxAttempts = maxAttempts;
13+
}
14+
15+
@Override
16+
public boolean canRetry(RetryContext context) {
17+
int attempts = context.getRetryCount();
18+
return attempts < maxAttempts;
19+
}
20+
21+
@Override
22+
public RetryContext open(RetryContext parent) {
23+
return new RetryContextSupport(parent);
24+
}
25+
26+
@Override
27+
public void close(RetryContext context) {
28+
// Do nothing
29+
}
30+
31+
@Override
32+
public void registerThrowable(RetryContext context, Throwable throwable) {
33+
RetryContextSupport retryContext = (RetryContextSupport) context;
34+
retryContext.registerThrowable(throwable);
35+
}
36+
37+
@Override
38+
public String toString() {
39+
return "CustomRetryPolicy [maxAttempts=" + maxAttempts + "]";
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.bootcamptoprod.retry.service;
2+
3+
import com.bootcamptoprod.retry.entity.Movie;
4+
import com.bootcamptoprod.retry.rest.client.MovieApiClient;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.retry.RetryCallback;
7+
import org.springframework.retry.support.RetryTemplate;
8+
import org.springframework.stereotype.Service;
9+
import org.springframework.web.client.HttpClientErrorException;
10+
import org.springframework.web.client.HttpServerErrorException;
11+
import org.springframework.web.client.ResourceAccessException;
12+
import org.springframework.web.client.RestClientException;
13+
14+
@Service
15+
public class MovieService {
16+
17+
@Autowired
18+
private MovieApiClient movieApiClient;
19+
20+
@Autowired
21+
private RetryTemplate retryTemplate;
22+
23+
public Movie getMovieDetails(String movieId) {
24+
return retryTemplate.execute((RetryCallback<Movie, RestClientException>) context -> {
25+
Movie movie = null;
26+
try {
27+
movie = movieApiClient.getMovieDetails(movieId);
28+
} catch (HttpServerErrorException httpServerErrorException) {
29+
System.out.println("Received HTTP server error exception while fetching the movie details. Error Message: " + httpServerErrorException.getMessage());
30+
throw httpServerErrorException;
31+
} catch (HttpClientErrorException httpClientErrorException) {
32+
System.out.println("Received HTTP client error exception while fetching the movie details. Error Message: " + httpClientErrorException.getMessage());
33+
throw httpClientErrorException;
34+
} catch (ResourceAccessException resourceAccessException) {
35+
System.out.println("Received Resource Access exception while fetching the movie details.");
36+
throw resourceAccessException;
37+
}
38+
return movie;
39+
});
40+
}
41+
}

0 commit comments

Comments
 (0)