Package org.springframework.batch.core.step.item

Source Code of org.springframework.batch.core.step.item.BatchRetryTemplate$InnerRetryTemplate

/*
* Copyright 2006-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.batch.core.step.item;

import org.springframework.classify.Classifier;
import org.springframework.retry.ExhaustedRetryException;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.RetryOperations;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.RetryState;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.context.RetryContextSupport;
import org.springframework.retry.policy.RetryContextCache;
import org.springframework.retry.support.DefaultRetryState;
import org.springframework.retry.support.RetrySynchronizationManager;
import org.springframework.retry.support.RetryTemplate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
* A special purpose retry template that deals specifically with multi-valued
* stateful retry. This is useful in the case where the operation to be retried
* operates on multiple items, and when it fails there is no way to decide which
* (if any) of the items was responsible. The {@link RetryState} used in the
* execute methods is composite, and when a failure occurs, all of the keys in
* the composite are "tarred with the same brush". Subsequent attempts to
* execute with any of the keys that have failed previously results in a new
* attempt and the previous state is used to check the {@link RetryPolicy}. If
* one of the failed items eventually succeeds then the others in the current
* composite for that attempt will be cleared from the context cache (as
* normal), but there may still be entries in the cache for the original failed
* items. This might mean that an item that did not cause a failure is never
* retried because other items in the same batch fail fatally first.
*
* @author Dave Syer
*
*/
public class BatchRetryTemplate implements RetryOperations {

  private class BatchRetryState extends DefaultRetryState {

    private final Collection<RetryState> keys;

    public BatchRetryState(Collection<RetryState> keys) {
      super(keys);
      this.keys = new ArrayList<RetryState>(keys);
    }

  }

  @SuppressWarnings("serial")
  private static class BatchRetryContext extends RetryContextSupport {

    private final Collection<RetryContext> contexts;

    public BatchRetryContext(RetryContext parent, Collection<RetryContext> contexts) {

      super(parent);

      this.contexts = contexts;
      int count = 0;

      for (RetryContext context : contexts) {
        int retryCount = context.getRetryCount();
        if (retryCount > count) {
          count = retryCount;
          registerThrowable(context.getLastThrowable());
        }
      }

    }

  }

  private static class InnerRetryTemplate extends RetryTemplate {

    @Override
    protected boolean canRetry(RetryPolicy retryPolicy, RetryContext context) {

      BatchRetryContext batchContext = (BatchRetryContext) context;

      for (RetryContext nextContext : batchContext.contexts) {
        if (!super.canRetry(retryPolicy, nextContext)) {
          return false;
        }
      }

      return true;

    }

    @Override
    protected RetryContext open(RetryPolicy retryPolicy, RetryState state) {

      BatchRetryState batchState = (BatchRetryState) state;

      Collection<RetryContext> contexts = new ArrayList<RetryContext>();
      for (RetryState retryState : batchState.keys) {
        contexts.add(super.open(retryPolicy, retryState));
      }

      return new BatchRetryContext(RetrySynchronizationManager.getContext(), contexts);

    }

    @Override
    protected void registerThrowable(RetryPolicy retryPolicy, RetryState state, RetryContext context, Throwable e) {

      BatchRetryState batchState = (BatchRetryState) state;
      BatchRetryContext batchContext = (BatchRetryContext) context;

      Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
      for (RetryState retryState : batchState.keys) {
        RetryContext nextContext = contextIterator.next();
        super.registerThrowable(retryPolicy, retryState, nextContext, e);
      }

    }

    @Override
    protected void close(RetryPolicy retryPolicy, RetryContext context, RetryState state, boolean succeeded) {

      BatchRetryState batchState = (BatchRetryState) state;
      BatchRetryContext batchContext = (BatchRetryContext) context;

      Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
      for (RetryState retryState : batchState.keys) {
        RetryContext nextContext = contextIterator.next();
        super.close(retryPolicy, nextContext, retryState, succeeded);
      }

    }

    @Override
    protected <T> T handleRetryExhausted(RecoveryCallback<T> recoveryCallback, RetryContext context,
        RetryState state) throws Throwable {

      BatchRetryState batchState = (BatchRetryState) state;
      BatchRetryContext batchContext = (BatchRetryContext) context;

      // Accumulate exceptions to be thrown so all the keys get a crack
      Throwable rethrowable = null;
      ExhaustedRetryException exhausted = null;

      Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
      for (RetryState retryState : batchState.keys) {

        RetryContext nextContext = contextIterator.next();

        try {
          super.handleRetryExhausted(null, nextContext, retryState);
        }
        catch (ExhaustedRetryException e) {
          exhausted = e;
        }
        catch (Throwable e) {
          rethrowable = e;
        }

      }

      if (recoveryCallback != null) {
        return recoveryCallback.recover(context);
      }

      if (exhausted != null) {
        throw exhausted;
      }

      throw rethrowable;

    }

  }

  private final InnerRetryTemplate delegate = new InnerRetryTemplate();

  private final RetryTemplate regular = new RetryTemplate();

  private RetryPolicy retryPolicy;

  public <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, Collection<RetryState> states) throws E,
  Exception {
    RetryState batchState = new BatchRetryState(states);
    return delegate.execute(retryCallback, batchState);
  }

  public <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback,
      Collection<RetryState> states) throws E, Exception {
    RetryState batchState = new BatchRetryState(states);
    return delegate.execute(retryCallback, recoveryCallback, batchState);
  }

  @Override
  public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback,
      RetryState retryState) throws E {
    return regular.execute(retryCallback, recoveryCallback, retryState);
  }

  @Override
  public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback) throws E {
    return regular.execute(retryCallback, recoveryCallback);
  }

  @Override
  public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RetryState retryState) throws E,
  ExhaustedRetryException {
    return regular.execute(retryCallback, retryState);
  }

  @Override
  public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E {
    return regular.execute(retryCallback);
  }

  public static List<RetryState> createState(List<?> keys) {
    List<RetryState> states = new ArrayList<RetryState>();
    for (Object key : keys) {
      states.add(new DefaultRetryState(key));
    }
    return states;
  }

  public static List<RetryState> createState(List<?> keys, Classifier<? super Throwable, Boolean> classifier) {
    List<RetryState> states = new ArrayList<RetryState>();
    for (Object key : keys) {
      states.add(new DefaultRetryState(key, classifier));
    }
    return states;
  }

  public void registerListener(RetryListener listener) {
    delegate.registerListener(listener);
    regular.registerListener(listener);
  }

  public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
    delegate.setBackOffPolicy(backOffPolicy);
    regular.setBackOffPolicy(backOffPolicy);
  }

  public void setListeners(RetryListener[] listeners) {
    delegate.setListeners(listeners);
    regular.setListeners(listeners);
  }

  public void setRetryContextCache(RetryContextCache retryContextCache) {
    delegate.setRetryContextCache(retryContextCache);
    regular.setRetryContextCache(retryContextCache);
  }

  public void setRetryPolicy(RetryPolicy retryPolicy) {
    this.retryPolicy = retryPolicy;
    delegate.setRetryPolicy(retryPolicy);
    regular.setRetryPolicy(retryPolicy);
  }

  public boolean canRetry(RetryContext context) {
    return context==null ? true : retryPolicy.canRetry(context);
  }

}
TOP

Related Classes of org.springframework.batch.core.step.item.BatchRetryTemplate$InnerRetryTemplate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.