Skip to content

Commit

Permalink
retry template for rest template
Browse files Browse the repository at this point in the history
  • Loading branch information
vietnguyengit committed Sep 26, 2024
1 parent be041f7 commit 92da832
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 17 deletions.
4 changes: 4 additions & 0 deletions ardcvocabs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
<artifactId>jsonassert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,48 @@
import au.org.aodn.ardcvocabs.service.ArdcVocabServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;

@Slf4j
@AutoConfiguration // More expressive vs @Configuration
@AutoConfiguration
@ConditionalOnMissingBean(ArdcVocabService.class)
@EnableRetry // Enable retry support
public class ArdcAutoConfiguration {

@Bean
public ArdcVocabService createArdcVocabsService(RestTemplate restTemplate) {
public ArdcVocabService createArdcVocabsService(RestTemplate restTemplate, RetryTemplate retryTemplate) {
log.info("Create ArdcVocabsService");
return new ArdcVocabServiceImpl(restTemplate);
return new ArdcVocabServiceImpl(restTemplate, retryTemplate);
}
/**
* In case the one who use this lib have not created it.
* @return RestTemplate
*/

@Bean
@ConditionalOnMissingBean(RestTemplate.class)
public RestTemplate ardcVocabRestTemplate() {
return new RestTemplate();
}

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

// Configure retry policy (3 attempts)
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
retryTemplate.setRetryPolicy(retryPolicy);

// Configure backoff policy (exponential backoff starting at 1 second, doubling each time)
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(1000); // 1 second
backOffPolicy.setMultiplier(2); // 2x each retry
backOffPolicy.setMaxInterval(5000); // max 5 seconds
retryTemplate.setBackOffPolicy(backOffPolicy);

return retryTemplate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

Expand All @@ -23,14 +24,16 @@ public class ArdcVocabServiceImpl implements ArdcVocabService {
protected String vocabApiBase;

protected RestTemplate restTemplate;
protected RetryTemplate retryTemplate;

protected Function<JsonNode, String> label = (node) -> node.has("prefLabel") ? node.get("prefLabel").get("_value").asText() : null;
protected Function<JsonNode, String> about = (node) -> node.has("_about") ? node.get("_about").asText() : null;
protected Function<JsonNode, String> definition = (node) -> node.has("definition") ? node.get("definition").asText() : null;
protected BiFunction<JsonNode, String, Boolean> isNodeValid = (node, item) -> node != null && !node.isEmpty() && node.has(item) && !node.get(item).isEmpty();

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

protected VocabModel buildVocabByResourceUri(String vocabUri, String vocabApiBase, VocabApiPaths vocabApiPaths) {
Expand All @@ -42,7 +45,7 @@ protected VocabModel buildVocabByResourceUri(String vocabUri, String vocabApiBas

try {
log.debug("Query api -> {}", detailsUrl);
ObjectNode detailsObj = restTemplate.getForObject(detailsUrl, ObjectNode.class);
ObjectNode detailsObj = retryTemplate.execute(context -> restTemplate.getForObject(detailsUrl, ObjectNode.class));
if(isNodeValid.apply(detailsObj, "result") && isNodeValid.apply(detailsObj.get("result"), "primaryTopic")) {
JsonNode target = detailsObj.get("result").get("primaryTopic");

Expand Down Expand Up @@ -106,7 +109,8 @@ protected Map<String, List<VocabModel>> getVocabLeafNodes(String vocabApiBase, V
while (url != null && !url.isEmpty()) {
try {
log.debug("getVocabLeafNodes -> {}", url);
ObjectNode r = restTemplate.getForObject(url, ObjectNode.class);
String finalUrl = url;
ObjectNode r = retryTemplate.execute(context -> restTemplate.getForObject(finalUrl, ObjectNode.class));

if (r != null && !r.isEmpty()) {
JsonNode node = r.get("result");
Expand All @@ -117,8 +121,7 @@ protected Map<String, List<VocabModel>> getVocabLeafNodes(String vocabApiBase, V
String dl = String.format(vocabApiBase + vocabApiPaths.getVocabDetailsApiPath(), about.apply(j));
try {
log.debug("getVocabLeafNodes -> {}", dl);
ObjectNode d = restTemplate.getForObject(dl, ObjectNode.class);

ObjectNode d = retryTemplate.execute(context -> restTemplate.getForObject(dl, ObjectNode.class));
if(isNodeValid.apply(d, "result") && isNodeValid.apply(d.get("result"), "primaryTopic")) {
JsonNode target = d.get("result").get("primaryTopic");

Expand Down Expand Up @@ -203,8 +206,8 @@ public List<VocabModel> getVocabTreeFromArdcByType(VocabApiPaths vocabApiPaths)
while (url != null && !url.isEmpty()) {
try {
log.debug("Query api -> {}", url);
ObjectNode r = restTemplate.getForObject(url, ObjectNode.class);

String finalUrl = url;
ObjectNode r = retryTemplate.execute(context -> restTemplate.getForObject(finalUrl, ObjectNode.class));
if (r != null && !r.isEmpty()) {
JsonNode node = r.get("result");
if (!node.isEmpty() && node.has("items") && !node.get("items").isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;

import java.io.FileNotFoundException;
Expand All @@ -37,6 +38,9 @@ public class ArdcVocabServiceImplTest extends BaseTestClass {
@Mock
RestTemplate mockRestTemplate;

@Mock
RetryTemplate mockRetryTemplate;

/**
* Check the url and return the canned file content
* @param template
Expand Down Expand Up @@ -191,12 +195,13 @@ else if(url.contains("/aodn-organisation-category-vocabulary/version-2-5/resourc
public void init() {
// If you want real download for testing, uncomment below and do not use mock
//this.ardcVocabService = new ArdcVocabServiceImpl(new RestTemplate());
this.ardcVocabService = new ArdcVocabServiceImpl(mockRestTemplate);
this.ardcVocabService = new ArdcVocabServiceImpl(mockRestTemplate, mockRetryTemplate);
this.ardcVocabService.vocabApiBase = "https://vocabs.ardc.edu.au/repository/api/lda/aodn";
}

@AfterEach void clear() {
Mockito.reset(mockRestTemplate);
Mockito.reset(mockRetryTemplate);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
Expand All @@ -20,11 +21,12 @@ public class VocabServiceTestConfig {
public ArdcVocabService createMockArdcVocabService() throws IOException {

RestTemplate template = Mockito.mock(RestTemplate.class);
RetryTemplate retryTemplate = Mockito.mock(RetryTemplate.class);

ArdcVocabServiceImplTest.setupPlatformMockRestTemplate(template);
ArdcVocabServiceImplTest.setupParameterVocabMockRestTemplate(template);
ArdcVocabServiceImplTest.setupOrganizationMockRestTemplate(template);

return new ArdcVocabServiceImpl(template);
return new ArdcVocabServiceImpl(template, retryTemplate);
}
}

0 comments on commit 92da832

Please sign in to comment.