/** * Construct a new {@link RepeatContextCounter}. * * @param context the current context. * @param countKey the key to use to store the counter in the context. * @param useParent true if the counter is to be shared between siblings. * The state will be stored in the parent of the context (if it exists) * instead of the context itself. */ public RepeatContextCounter(RepeatContext context, String countKey, boolean useParent) { super(); Assert.notNull(context, "The context must be provided to initialize a counter"); this.countKey = countKey; this.useParent = useParent; RepeatContext parent = context.getParent(); if (this.useParent && parent != null) { this.context = parent; } else { this.context = context; } if (!this.context.hasAttribute(countKey)) { this.context.setAttribute(countKey, new AtomicInteger()); } }
private AtomicInteger getCounter() { return ((AtomicInteger) context.getAttribute(countKey)); }
/** * Get the next completed result, possibly executing several callbacks until * one finally finishes. Normally a subclass would have to override both * this method and {@link #createInternalState(RepeatContext)} because the * implementation of this method would rely on the details of the internal * state. * * @param context current BatchContext. * @param callback the callback to execute. * @param state maintained by the implementation. * @return a finished result. * @throws Throwable any Throwable emitted during the iteration * * @see #isComplete(RepeatContext) * @see #createInternalState(RepeatContext) */ protected RepeatStatus getNextResult(RepeatContext context, RepeatCallback callback, RepeatInternalState state) throws Throwable { update(context); if (logger.isDebugEnabled()) { logger.debug("Repeat operation about to start at count=" + context.getStartedCount()); } return callback.doInIteration(context); }
private boolean isMarkedComplete(RepeatContext context) { boolean complete = context.isCompleteOnly(); if (context.getParent() != null) { complete = complete || isMarkedComplete(context.getParent()); } if (complete) { logger.debug("Repeat is complete according to context alone."); } return complete; }
/** * Set current session and all ancestors (via parent) to complete., */ public static void setAncestorsCompleteOnly() { RepeatContext context = getContext(); while (context != null) { context.setCompleteOnly(); context = context.getParent(); } }
/** * Get the parent context (the retry is in an inner "chunk" loop and we want * the exception to be handled at the outer "step" level). * @return the {@link RepeatContext} that should hold the exhausted flag. */ private RepeatContext getRepeatContext() { RepeatContext context = RepeatSynchronizationManager.getContext(); if (context.getParent() != null) { return context.getParent(); } return context; }
/** * Check if the exception is going to be retried, and veto the handling if * it is. If retry is exhausted or the exception is on the fatal list, then * handle using the delegate. * * @see ExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, * java.lang.Throwable) */ @Override public void handleException(RepeatContext context, Throwable throwable) throws Throwable { // Only bother to check the delegate exception handler if we know that // retry is exhausted if (fatalExceptionClassifier.classify(throwable) || context.hasAttribute(EXHAUSTED)) { logger.debug("Handled fatal exception"); exceptionHandler.handleException(context, throwable); } else { logger.debug("Handled non-fatal exception", throwable); } }
/** * Test method for * {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)} . */ public void testNoRethrowWhenRetryNotExhausted() throws Throwable { RetryPolicy retryPolicy = new AlwaysRetryPolicy(); RuntimeException ex = new RuntimeException("foo"); SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex, Collections .<Class<? extends Throwable>> singleton(Error.class)); // Then pretend to handle the exception in the parent context... handler.handleException(context.getParent(), ex); assertEquals(0, context.attributeNames().length); assertEquals(0, context.getParent().attributeNames().length); }
/** * If retry is exhausted set up some state in the context that can be used * to signal that the exception should be handled. * * @see org.springframework.retry.RetryListener#close(org.springframework.retry.RetryContext, * org.springframework.retry.RetryCallback, java.lang.Throwable) */ @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { if (!retryPolicy.canRetry(context)) { if (logger.isDebugEnabled()) { logger.debug("Marking retry as exhausted: "+context); } getRepeatContext().setAttribute(EXHAUSTED, "true"); } }
/** * Convenience method to set the current repeat operation to complete if it * exists. */ public static void setCompleteOnly() { RepeatContext context = getContext(); if (context != null) { context.setCompleteOnly(); } }
@Test public void testNotRethrownWithThreshold() throws Throwable { handler.setThresholds(Collections.<Class<? extends Throwable>, Integer> singletonMap(Exception.class, 1)); // No exception... handler.handleException(context, new RuntimeException("Foo")); AtomicInteger counter = (AtomicInteger) context.getAttribute(context.attributeNames()[0]); assertNotNull(counter); assertEquals(1, counter.get()); }
public void testSetSessionCompleteOnlyWithParent() { assertNull(RepeatSynchronizationManager.getContext()); RepeatContext child = new RepeatContextSupport(context); RepeatSynchronizationManager.register(child); assertFalse(child.isCompleteOnly()); RepeatSynchronizationManager.setAncestorsCompleteOnly(); assertTrue(child.isCompleteOnly()); assertTrue(context.isCompleteOnly()); }
@Override public RepeatStatus doInIteration(RepeatContext context) throws Exception { count++; assertNotNull(context); assertNotSame("Nested batch should have new session", context, context.getParent()); assertSame(context, RepeatSynchronizationManager.getContext()); return RepeatStatus.FINISHED; } }) {
public void testAttributeCreatedInParent() throws Exception { new RepeatContextCounter(context, "FOO", true); assertFalse(context.hasAttribute("FOO")); assertTrue(parent.hasAttribute("FOO")); }
/** * Test method for * {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)} . */ public void testRethrowWhenFatal() throws Throwable { RetryPolicy retryPolicy = new AlwaysRetryPolicy(); RuntimeException ex = new RuntimeException("foo"); SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex, Collections .<Class<? extends Throwable>> singleton(RuntimeException.class)); // Then pretend to handle the exception in the parent context... try { handler.handleException(context.getParent(), ex); fail("Expected RuntimeException"); } catch (RuntimeException e) { assertEquals(ex, e); } assertEquals(0, context.attributeNames().length); // One for the counter in the delegate exception handler assertEquals(1, context.getParent().attributeNames().length); }
private boolean isMarkedComplete(RepeatContext context) { boolean complete = context.isCompleteOnly(); if (context.getParent() != null) { complete = complete || isMarkedComplete(context.getParent()); } if (complete) { logger.debug("Repeat is complete according to context alone."); } return complete; }
/** * Set current session and all ancestors (via parent) to complete., */ public static void setAncestorsCompleteOnly() { RepeatContext context = getContext(); while (context != null) { context.setCompleteOnly(); context = context.getParent(); } }
/** * If retry is exhausted set up some state in the context that can be used * to signal that the exception should be handled. * * @see org.springframework.batch.retry.RetryListener#close(org.springframework.batch.retry.RetryContext, * org.springframework.batch.retry.RetryCallback, java.lang.Throwable) */ public <T> void close(RetryContext context, RetryCallback<T> callback, Throwable throwable) { if (!retryPolicy.canRetry(context)) { logger.debug("Marking retry as exhausted: "+context); getRepeatContext().setAttribute(EXHAUSTED, "true"); } }
@Override public RepeatStatus doInIteration(RepeatContext context) throws Exception { count++; context.setCompleteOnly(); return RepeatStatus.FINISHED; } });
public void testSetSessionCompleteOnly() { assertNull(RepeatSynchronizationManager.getContext()); RepeatSynchronizationManager.register(context); assertFalse(RepeatSynchronizationManager.getContext().isCompleteOnly()); RepeatSynchronizationManager.setCompleteOnly(); assertTrue(RepeatSynchronizationManager.getContext().isCompleteOnly()); }