/**
  * Clean up the cache if necessary and close the context provided (if the flag indicates that
  * processing was successful).
  *
  * @param retryPolicy the {@link RetryPolicy}
  * @param context the {@link RetryContext}
  * @param state the {@link RetryState}
  * @param succeeded whether the close succeeded
  */
 protected void close(
     RetryPolicy retryPolicy, RetryContext context, RetryState state, boolean succeeded) {
   if (state != null) {
     if (succeeded) {
       if (!context.hasAttribute(GLOBAL_STATE)) {
         this.retryContextCache.remove(state.getKey());
       }
       retryPolicy.close(context);
       context.setAttribute(RetryContext.CLOSED, true);
     }
   } else {
     retryPolicy.close(context);
     context.setAttribute(RetryContext.CLOSED, true);
   }
 }
 private RetryContext doOpenInternal(RetryPolicy retryPolicy, RetryState state) {
   RetryContext context = retryPolicy.open(RetrySynchronizationManager.getContext());
   if (state != null) {
     context.setAttribute(RetryContext.STATE_KEY, state.getKey());
   }
   if (context.hasAttribute(GLOBAL_STATE)) {
     registerContext(context, state);
   }
   return context;
 }
 protected void registerThrowable(
     RetryPolicy retryPolicy, RetryState state, RetryContext context, Throwable e) {
   retryPolicy.registerThrowable(context, e);
   registerContext(context, state);
 }
 /**
  * Decide whether to proceed with the ongoing retry attempt. This method is called before the
  * {@link RetryCallback} is executed, but after the backoff and open interceptors.
  *
  * @param retryPolicy the policy to apply
  * @param context the current retry context
  * @return true if we can continue with the attempt
  */
 protected boolean canRetry(RetryPolicy retryPolicy, RetryContext context) {
   return retryPolicy.canRetry(context);
 }