diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java index 1bb9a312e..bced00053 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java @@ -152,11 +152,10 @@ public class HibernateAwareItemWriter implements ItemWriter, InitializingBean { flush = failed.contains(output); } if (flush) { - RepeatContext context = RepeatSynchronizationManager.getContext(); // Force early completion to commit aggressively if we encounter a // failed item (from a failed chunk but we don't know which one was // the problem). - context.setCompleteOnly(); + RepeatSynchronizationManager.setCompleteOnly(); // Flush now, so that if there is a failure this record can be // skipped. doHibernateFlush(); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/support/RepeatSynchronizationManager.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/support/RepeatSynchronizationManager.java index c1ec8f9aa..7b9bffba8 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/support/RepeatSynchronizationManager.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/support/RepeatSynchronizationManager.java @@ -21,12 +21,14 @@ import org.springframework.batch.repeat.RepeatContext; import org.springframework.batch.repeat.RepeatOperations; /** - * Global variable support for batch clients. Normally it is not necessary for - * batch clients to be aware of the surrounding batch environment because a + * Global variable support for repeat clients. Normally it is not necessary for + * clients to be aware of the surrounding environment because a * {@link RepeatCallback} can always use the context it is passed by the * enclosing {@link RepeatOperations}. But occasionally it might be helpful to * have lower level access to the ongoing {@link RepeatContext} so we provide a - * global accessor here. + * global accessor here. The mutator methods ({@link #clear()} and + * {@link #register(RepeatContext)} should not be used except internally by + * {@link RepeatOperations} implementations. * * @author Dave Syer * @@ -35,6 +37,9 @@ public class RepeatSynchronizationManager { private static final ThreadLocal contextHolder = new ThreadLocal(); + private RepeatSynchronizationManager() { + } + /** * Getter for the current context. A context is shared by all items in the * batch, so this method is intended to return the same context object @@ -49,7 +54,8 @@ public class RepeatSynchronizationManager { } /** - * Convenience method to set the current batch session to complete. + * Convenience method to set the current repeat operation to complete if it + * exists. */ public static void setCompleteOnly() { RepeatContext context = getContext(); @@ -73,8 +79,8 @@ public class RepeatSynchronizationManager { } /** - * Used internally by {@link RepeatOperations} implementations to clear the - * current context at the end of a batch. + * Clear the current context at the end of a batch - should only be used by + * {@link RepeatOperations} implementations. * * @return the old value if there was one. */ diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java index a4800c1d7..8242aa027 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java @@ -42,7 +42,7 @@ public interface RetryContext extends AttributeAccessor { boolean isExhaustedOnly(); /** - * Accesssor for the parent context if retry blocks are nested. + * Accessor for the parent context if retry blocks are nested. * * @return the parent or null if there is none. */ diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetrySynchronizationManager.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetrySynchronizationManager.java index e24f10686..b17a5779b 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetrySynchronizationManager.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetrySynchronizationManager.java @@ -16,26 +16,61 @@ package org.springframework.batch.retry.support; +import org.springframework.batch.repeat.RepeatContext; +import org.springframework.batch.repeat.RepeatOperations; +import org.springframework.batch.retry.RetryCallback; import org.springframework.batch.retry.RetryContext; +import org.springframework.batch.retry.RetryOperations; +/** + * Global variable support for retry clients. Normally it is not necessary for + * clients to be aware of the surrounding environment because a + * {@link RetryCallback} can always use the context it is passed by the + * enclosing {@link RetryOperations}. But occasionally it might be helpful to + * have lower level access to the ongoing {@link RetryContext} so we provide a + * global accessor here. The mutator methods ({@link #clear()} and + * {@link #register(RepeatContext)} should not be used except internally by + * {@link RetryOperations} implementations. + * + * @author Dave Syer + * + */ public class RetrySynchronizationManager { - private RetrySynchronizationManager() { - } + private RetrySynchronizationManager() {} private static final ThreadLocal context = new ThreadLocal(); + /** + * Public accessor for the locally enclosing {@link RetryContext}. + * + * @return the current retry context, or null if there isn't one + */ public static RetryContext getContext() { RetryContext result = (RetryContext) context.get(); return result; } + /** + * Method for registering a context - should only be used by + * {@link RetryOperations} implementations to ensure that + * {@link #getContext()} always returns the correct value. + * + * @param context the new context to register + * @return the old context if there was one + */ public static RetryContext register(RetryContext context) { RetryContext oldContext = getContext(); RetrySynchronizationManager.context.set(context); return oldContext; } + /** + * Clear the current context at the end of a batch - should only be used by + * {@link RepeatOperations} implementations. + * + * @return the old value if there was one. + */ public static RetryContext clear() { RetryContext value = getContext(); RetryContext parent = value == null ? null : value.getParent(); @@ -43,14 +78,4 @@ public class RetrySynchronizationManager { return value; } - public static RetryContext clearAll() { - RetryContext result = null; - RetryContext context = clear(); - while (context != null) { - result = context; - context = clear(); - } - return result; - } - } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetrySynchronizationManagerTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetrySynchronizationManagerTests.java index 971752a14..274ab131c 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetrySynchronizationManagerTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetrySynchronizationManagerTests.java @@ -35,7 +35,7 @@ public class RetrySynchronizationManagerTests extends TestCase { protected void setUp() throws Exception { super.setUp(); - RetrySynchronizationManager.clearAll(); + RetrySynchronizationManagerTests.clearAll(); RetryContext status = RetrySynchronizationManager.getContext(); assertNull(status); } @@ -80,4 +80,20 @@ public class RetrySynchronizationManagerTests extends TestCase { RetryContext child = new RetryContextSupport(parent); assertSame(parent, child.getParent()); } + + /** + * Clear all contexts starting with the current one and continuing until + * {@link RetrySynchronizationManager#clear()} returns null. + * + * @return + */ + public static RetryContext clearAll() { + RetryContext result = null; + RetryContext context = RetrySynchronizationManager.clear(); + while (context != null) { + result = context; + context = RetrySynchronizationManager.clear(); + } + return result; + } }