0

I'm trying to use Spring Retry for Jackson deserialization into a POJO object like so:

@Data
public class myPOJO {
    private String cmpStr = "test";
    private String myStr;

    @Retryable(maxAttempts=3, value=RuntimeException.class, backoff=@Backoff(delay=3000))
    @JsonProperty("jsonElement")
    private void retryableFunc(Map<String, Object> jsonElement) {
        try {
            myStr = jsonElement.get("jsonAttribute");
            if (!Objects.equals(myStr, cmpStr))
                throw new RuntimeException("Bad Response");
        }
        catch (Exception e) {
            throw new RuntimeException("Bad Response");
        }
    }

    @Recover
    private void recover(Exception e) {
        System.out.println("Recover triggered");
    }
}

MyPOJO is instantiated like this:

RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singleTonList(MediaType.APPLICATION_JSON));
String jsonAttribute = restTemplate.exchange(URL, HttpMethod.GET, new HttpEntity<>(headers), myPOJO.class).getBody().getMyStr();

Main app looks like:

@SpringBootApplication
@EnableRetry
public class mainApplication {
    public static void main(String[] args) {
        SpringApplication.run(mainApplication.class, args);
    }
}

JSON response looks like this:

{
    "jsonElement": {
        "jsonAttribute": "test1"
    }
}

Retry is never triggered, but the exception is thrown:

Error while extracting response for type myPOJO and content type [application/json;charset=utf-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Bad Response; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Bad Response

davo
  • 35
  • 4

1 Answers1

0

I think you have some basic misunderstanding. Spring Retry can only be applied to beans that Spring manages. Your POJO might be created by a Spring component (RestTemplate), it's not a managed @Bean in the application context.

You can't use annotation-based retry on objects that are not managed by the application context.

You still didn't show how you are calling the retryableFunc but you should, instead, call your object from some helper bean:

public class MyHelper {

    @Retryable(...)
    public void validateMyPojo(MyPojo pojo, Map<...,...> jsonElement) {
        pojo.retryableFunc(jsonElement);
    }

}

and

@Bean
public MyHelper myHelper() {
    return new MyHelper();
}

Or, you can simply use a RetryTemplate to call the method.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Thanks for your help! I actually never call ```retryableFunc()``` directly. I have it annotated with ```@JsonProperty``` so that Jackson handles JSON deserialization, but I guess I can't do this and retry in one step – davo Jun 26 '19 at 15:05
  • Ah - I didn't pay attention to the json annotation. But it's not clear why you want retry there - it will fail every time. – Gary Russell Jun 26 '19 at 15:08
  • I basically want the same RestTemplate call to be made until the JSON response changes or max retries is reached. I guess I can just do this without Retryable – davo Jun 26 '19 at 15:14
  • Ah that's different; if you want to keep fetching you need to put `String jsonAttribute = restTemplate.exchange(URL, HttpMethod.GET, new HttpEntity<>(headers), myPOJO.class).getBody().getMyStr();` in a `@Retryable` method. You also probably don't want to keep creating a new `RestTemplate` each time; you only need one for the life of the application. – Gary Russell Jun 26 '19 at 15:24