IN PROGRESS - issue BATCH-201: Move responsibility for deciding if an exception terminates a batch to ExceptionHandler
http://opensource.atlassian.com/projects/spring/browse/BATCH-201 Nearly finished - ExceptionHandler is the owner of abnormal termination.
This commit is contained in:
@@ -162,7 +162,7 @@ public class SimpleJobExecutorFacadeTests extends TestCase {
|
||||
try {
|
||||
jobExecutorFacade.start(jobExecution);
|
||||
} catch (NoSuchJobConfigurationException e) {
|
||||
System.err.println("Shouldn't happen");
|
||||
throw new IllegalStateException("Shouldn't happen");
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.batch.execution.launch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -71,15 +70,15 @@ public class SimpleJobTests extends TestCase {
|
||||
|
||||
private ItemProvider provider;
|
||||
|
||||
private DefaultJobExecutor jobLifecycle = new DefaultJobExecutor();;
|
||||
private DefaultJobExecutor jobExecutor = new DefaultJobExecutor();;
|
||||
|
||||
private DefaultStepExecutor stepLifecycle = new DefaultStepExecutor();
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
jobLifecycle.setJobRepository(repository);
|
||||
jobExecutor.setJobRepository(repository);
|
||||
stepLifecycle.setRepository(repository);
|
||||
jobLifecycle.setStepExecutorFactory(new StepExecutorFactory() {
|
||||
jobExecutor.setStepExecutorFactory(new StepExecutorFactory() {
|
||||
public StepExecutor getExecutor(StepConfiguration configuration) {
|
||||
return stepLifecycle;
|
||||
}
|
||||
@@ -125,7 +124,7 @@ public class SimpleJobTests extends TestCase {
|
||||
|
||||
JobExecution jobExecutionContext = new JobExecution(job);
|
||||
|
||||
jobLifecycle.run(jobConfiguration, jobExecutionContext);
|
||||
jobExecutor.run(jobConfiguration, jobExecutionContext);
|
||||
assertEquals(BatchStatus.COMPLETED, job.getStatus());
|
||||
assertEquals(3, processed.size());
|
||||
assertTrue(processed.contains("foo"));
|
||||
@@ -136,13 +135,14 @@ public class SimpleJobTests extends TestCase {
|
||||
|
||||
JobConfiguration jobConfiguration = new JobConfiguration();
|
||||
JobIdentifier runtimeInformation = new SimpleJobIdentifier("real.job");
|
||||
final List throwables = new ArrayList();
|
||||
|
||||
RepeatTemplate chunkOperations = new RepeatTemplate();
|
||||
// Always handle the exception a check it is the right one...
|
||||
chunkOperations.setExceptionHandler(new ExceptionHandler() {
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) {
|
||||
assertEquals(1, throwables.size());
|
||||
assertEquals("Try again Dummy!", ((Throwable) throwables.iterator().next()).getMessage());
|
||||
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
|
||||
throwables.add(throwable);
|
||||
assertEquals("Try again Dummy!", throwable.getMessage());
|
||||
}
|
||||
});
|
||||
stepLifecycle.setChunkOperations(chunkOperations);
|
||||
@@ -170,8 +170,8 @@ public class SimpleJobTests extends TestCase {
|
||||
jobConfiguration.addStep(step);
|
||||
|
||||
JobInstance job = repository.findOrCreateJob(jobConfiguration, runtimeInformation);
|
||||
JobExecution jobExecutionContext = new JobExecution(job);
|
||||
jobLifecycle.run(jobConfiguration, jobExecutionContext);
|
||||
JobExecution jobExecution = new JobExecution(job);
|
||||
jobExecutor.run(jobConfiguration, jobExecution);
|
||||
|
||||
assertEquals(BatchStatus.COMPLETED, job.getStatus());
|
||||
assertEquals(0, processed.size());
|
||||
@@ -196,7 +196,7 @@ public class SimpleJobTests extends TestCase {
|
||||
JobInstance job = repository.findOrCreateJob(jobConfiguration, runtimeInformation);
|
||||
JobExecution jobExecutionContext = new JobExecution(job);
|
||||
try {
|
||||
jobLifecycle.run(jobConfiguration, jobExecutionContext);
|
||||
jobExecutor.run(jobConfiguration, jobExecutionContext);
|
||||
fail("Expected RuntimeException");
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.batch.execution.step.simple;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -55,9 +54,9 @@ public class SimpleStepExecutorFactoryTests extends TestCase {
|
||||
SimpleStepConfiguration configuration = new SimpleStepConfiguration();
|
||||
final List list = new ArrayList();
|
||||
configuration.setExceptionHandler(new ExceptionHandler() {
|
||||
public void handleExceptions(RepeatContext context,
|
||||
Collection throwables) throws RuntimeException {
|
||||
list.addAll(throwables);
|
||||
public void handleException(RepeatContext context,
|
||||
Throwable throwable) throws RuntimeException {
|
||||
list.add(throwable);
|
||||
throw new RuntimeException("Oops");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -143,7 +143,7 @@ public class RestartableItemProviderTaskletTests extends TestCase {
|
||||
assertNotNull(data);
|
||||
// restore from restart data (see asserts in mock classes)
|
||||
module.restoreFrom(data);
|
||||
System.err.println(data.getProperties());
|
||||
//System.err.println(data.getProperties());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,20 +29,22 @@ public interface RepeatInterceptor {
|
||||
* Called by the framework before each batch item. Implementers can halt a
|
||||
* batch by setting the complete flag on the context.
|
||||
*
|
||||
* @param context the current batch context.
|
||||
* @param context
|
||||
* the current batch context.
|
||||
*/
|
||||
void before(RepeatContext context);
|
||||
|
||||
/**
|
||||
* Called by the framework after each item has been processed, including if
|
||||
* the item processing results in an exception, in which case result is an
|
||||
* Exception. This method is called as soon as the result is known, whereas
|
||||
* {@link #onError(RepeatContext, Throwable)} is only guaranteed to be
|
||||
* called at some time after the failure occurred.
|
||||
* Called by the framework after each item has been processed, unless the
|
||||
* item processing results in an exception. This method is called as soon as
|
||||
* the result is known.
|
||||
*
|
||||
* @param context the current batch context
|
||||
* @param result the result of the callback item - ExitStatus in normal
|
||||
* circumstances, but might be null or an Exception in abnormal cases.
|
||||
* @param context
|
||||
* the current batch context
|
||||
* @param result
|
||||
* the result of the callback item - ExitStatus in normal
|
||||
* circumstances, but might be null or an Exception in abnormal
|
||||
* cases.
|
||||
*/
|
||||
void after(RepeatContext context, Object result);
|
||||
|
||||
@@ -54,20 +56,23 @@ public interface RepeatInterceptor {
|
||||
* enclosing batches (the whole job), the would need to use the parent
|
||||
* context (recursively).
|
||||
*
|
||||
* @param context the current batch context
|
||||
* @param context
|
||||
* the current batch context
|
||||
*/
|
||||
void open(RepeatContext context);
|
||||
|
||||
/**
|
||||
* Called at the end of a batch if any callback fails by throwing an
|
||||
* exception. There will be one call to this method for each exception
|
||||
* thrown during a repeat operation (e.g. a chunk).<br/>
|
||||
* Called when a repeat callback fails by throwing an exception. There will
|
||||
* be one call to this method for each exception thrown during a repeat
|
||||
* operation (e.g. a chunk).<br/>
|
||||
*
|
||||
* There is no need to re-throw the exception here - that will be done by
|
||||
* the enclosing framework.
|
||||
*
|
||||
* @param context the current batch context
|
||||
* @param e the error that was encountered in an item callback.
|
||||
* @param context
|
||||
* the current batch context
|
||||
* @param e
|
||||
* the error that was encountered in an item callback.
|
||||
*/
|
||||
void onError(RepeatContext context, Throwable e);
|
||||
|
||||
@@ -76,8 +81,8 @@ public interface RepeatInterceptor {
|
||||
* completion (i.e. even after an exception). Implementers can use this
|
||||
* method to clean up any resources.
|
||||
*
|
||||
* @param context the current batch context.
|
||||
* @return TODO
|
||||
* @param context
|
||||
* the current batch context.
|
||||
*/
|
||||
void close(RepeatContext context);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
|
||||
@@ -38,12 +37,12 @@ public class CompositeExceptionHandler implements ExceptionHandler {
|
||||
* Iterate over the handlers delegating the call to each in turn. The chain
|
||||
* ends if an exception is thrown.
|
||||
*
|
||||
* @see ExceptionHandler#handleExceptions(RepeatContext, Collection)
|
||||
* @see ExceptionHandler#handleException(RepeatContext, Throwable)
|
||||
*/
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) throws RuntimeException {
|
||||
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
|
||||
for (int i = 0; i < handlers.length; i++) {
|
||||
ExceptionHandler handler = handlers[i];
|
||||
handler.handleExceptions(context, throwables);
|
||||
handler.handleException(context, throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.exception.RepeatException;
|
||||
@@ -31,26 +30,25 @@ import org.springframework.batch.repeat.exception.RepeatException;
|
||||
public class DefaultExceptionHandler implements ExceptionHandler {
|
||||
|
||||
/**
|
||||
* Rethrow the first throwable in the collection's iterator. Wrap in a
|
||||
* Rethrow the throwable in the collection's iterator. Wrap in a
|
||||
* {@link RepeatException} if the first instance is not a
|
||||
* {@link RuntimeException}.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.exception.handler.ExceptionHandler#handleExceptions(RepeatContext,
|
||||
* java.util.Collection)
|
||||
* @see org.springframework.batch.repeat.exception.handler.ExceptionHandler#handleException(RepeatContext,
|
||||
* Throwable)
|
||||
*/
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) throws RuntimeException {
|
||||
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
|
||||
|
||||
Throwable t = (Throwable) throwables.iterator().next();
|
||||
rethrow(t);
|
||||
rethrow(throwable);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to rethrow the Throwable instance. Wraps it in a
|
||||
* {@link RepeatException} if it is not a {@link RuntimeException}.
|
||||
* {@link RepeatException} if it is not a {@link Exception}.
|
||||
*
|
||||
* @param throwable a Throwable.
|
||||
* @throws RuntimeException if the throwable is a RuntimeException just
|
||||
* @throws RuntimeException if the throwable is a {@link RuntimeException} just
|
||||
* rethrow, otherwise wrap in a {@link RepeatException}
|
||||
*/
|
||||
public static void rethrow(Throwable throwable) throws RuntimeException {
|
||||
|
||||
@@ -16,18 +16,16 @@
|
||||
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
|
||||
/**
|
||||
* Policy to allow strategies for rethrowing exceptions in the case of
|
||||
* termination. Normally a {@link CompletionPolicy} will be used to decide
|
||||
* whether to end a batch including when there is an exception, and the
|
||||
* {@link ExceptionHandler} is used to distinguish between normal and abnormal
|
||||
* ending. An abnormal ending would normally result in an
|
||||
* {@link ExceptionHandler} throwing an exception.
|
||||
* Handler to allow strategies for rethrowing exceptions. Normally a
|
||||
* {@link CompletionPolicy} will be used to decide whether to end a batch when
|
||||
* there is no exception, and the {@link ExceptionHandler} is used to signal an
|
||||
* abnormal ending. An abnormal ending would normally result in an
|
||||
* {@link ExceptionHandler} throwing an exception. The caller will catch and
|
||||
* rethrow it if necessary.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
@@ -35,17 +33,21 @@ import org.springframework.batch.repeat.RepeatContext;
|
||||
public interface ExceptionHandler {
|
||||
|
||||
/**
|
||||
* Deal with a collection of throwables accumulated during a batch. The
|
||||
* collection may consist of RuntimeExceptions or other unchecked
|
||||
* exceptions.
|
||||
* @param context the current {@link RepeatContext}. Can be used to store
|
||||
* state (via attributes), for example to count the number of occurrences of
|
||||
* a particular exception type and implement a threshold policy.
|
||||
* @param throwables a collection of non-checked exceptions.
|
||||
* Deal with a Throwable during a batch. The input might be
|
||||
* RuntimeExceptions or other unchecked exceptions.
|
||||
*
|
||||
* @throws any or all of the exception types passed in, or a wrapped
|
||||
* exception if one is an error or checked exception.
|
||||
* @param context
|
||||
* the current {@link RepeatContext}. Can be used to store state
|
||||
* (via attributes), for example to count the number of
|
||||
* occurrences of a particular exception type and implement a
|
||||
* threshold policy.
|
||||
* @param throwable
|
||||
* an exception.
|
||||
* @throws RuntimeException
|
||||
* implementations must wrap and rethrow other Throwables
|
||||
* appropriately.
|
||||
*/
|
||||
void handleExceptions(RepeatContext context, Collection throwables) throws RuntimeException;
|
||||
void handleException(RepeatContext context, Throwable throwable)
|
||||
throws RuntimeException;
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.common.ExceptionClassifier;
|
||||
@@ -85,33 +82,26 @@ public class LogOrRethrowExceptionHandler implements ExceptionHandler {
|
||||
* Classify the throwables and decide whether to rethrow based on the
|
||||
* result. The context is not used.
|
||||
*
|
||||
* @throws Exception
|
||||
* @throws RuntimeException
|
||||
*
|
||||
* @see {@link ExceptionHandler#handleExceptions(RepeatContext, Collection)}
|
||||
* @see {@link ExceptionHandler#handleException(RepeatContext, Throwable)}
|
||||
*/
|
||||
public void handleExceptions(RepeatContext context, Collection throwables)
|
||||
public void handleException(RepeatContext context, Throwable throwable)
|
||||
throws RuntimeException {
|
||||
|
||||
for (Iterator iter = throwables.iterator(); iter.hasNext();) {
|
||||
Throwable throwable = (Throwable) iter.next();
|
||||
Object key = exceptionClassifier.classify(throwable);
|
||||
if (ERROR.equals(key)) {
|
||||
logger.error("Exception encountered in batch repeat.",
|
||||
throwable);
|
||||
} else if (WARN.equals(key)) {
|
||||
logger
|
||||
.warn("Exception encountered in batch repeat.",
|
||||
throwable);
|
||||
} else if (DEBUG.equals(key) && logger.isDebugEnabled()) {
|
||||
logger.debug("Exception encountered in batch repeat.",
|
||||
throwable);
|
||||
} else if (RETHROW.equals(key)) {
|
||||
DefaultExceptionHandler.rethrow(throwable);
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Unclassified exception encountered. Did you mean to classifiy this as 'rethrow'?",
|
||||
throwable);
|
||||
}
|
||||
Object key = exceptionClassifier.classify(throwable);
|
||||
if (ERROR.equals(key)) {
|
||||
logger.error("Exception encountered in batch repeat.", throwable);
|
||||
} else if (WARN.equals(key)) {
|
||||
logger.warn("Exception encountered in batch repeat.", throwable);
|
||||
} else if (DEBUG.equals(key) && logger.isDebugEnabled()) {
|
||||
logger.debug("Exception encountered in batch repeat.", throwable);
|
||||
} else if (RETHROW.equals(key)) {
|
||||
DefaultExceptionHandler.rethrow(throwable);
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Unclassified exception encountered. Did you mean to classifiy this as 'rethrow'?",
|
||||
throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
@@ -40,7 +39,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(RethrowOnThresholdExceptionHandler.class);
|
||||
protected final Log logger = LogFactory
|
||||
.getLog(RethrowOnThresholdExceptionHandler.class);
|
||||
|
||||
private ExceptionClassifier exceptionClassifier = new ExceptionClassifierSupport();
|
||||
|
||||
@@ -50,10 +50,11 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
|
||||
|
||||
/**
|
||||
* Flag to indicate the the exception counters should be shared between
|
||||
* sibling contexts in a nested batch. Default is false.
|
||||
* sibling contexts in a nested batch. Default is false.
|
||||
*
|
||||
* @param useParent true if the parent context should be used to store the
|
||||
* counters.
|
||||
* @param useParent
|
||||
* true if the parent context should be used to store the
|
||||
* counters.
|
||||
*/
|
||||
public void setUseParent(boolean useParent) {
|
||||
this.useParent = useParent;
|
||||
@@ -74,16 +75,21 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
|
||||
* are usually String literals, depending on the {@link ExceptionClassifier}
|
||||
* implementation used.
|
||||
*
|
||||
* @param thresholds the threshold value map.
|
||||
* @param thresholds
|
||||
* the threshold value map.
|
||||
*/
|
||||
public void setThresholds(Map thresholds) {
|
||||
for (Iterator iter = thresholds.entrySet().iterator(); iter.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) iter.next();
|
||||
if (!(entry.getKey() instanceof String)) {
|
||||
logger.warn("Key in thresholds map is not of type String: " + entry.getKey());
|
||||
logger.warn("Key in thresholds map is not of type String: "
|
||||
+ entry.getKey());
|
||||
}
|
||||
Assert.state(entry.getValue() instanceof Integer, "Threshold value must be of type Integer. "
|
||||
+ "Try using the value-type attribute if you care configuring this map via xml.");
|
||||
Assert
|
||||
.state(
|
||||
entry.getValue() instanceof Integer,
|
||||
"Threshold value must be of type Integer. "
|
||||
+ "Try using the value-type attribute if you care configuring this map via xml.");
|
||||
}
|
||||
this.thresholds = thresholds;
|
||||
}
|
||||
@@ -104,28 +110,27 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
|
||||
* Classify the throwables and decide whether to rethrow based on the
|
||||
* result. The context is used to accumulate the number of exceptions of the
|
||||
* same type according to the classifier.
|
||||
* @throws Exception
|
||||
*
|
||||
* @see {@link ExceptionHandler#handleExceptions(RepeatContext, Collection)}
|
||||
* @throws Exception
|
||||
* @see {@link ExceptionHandler#handleException(RepeatContext, Throwable)}
|
||||
*/
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) throws RuntimeException {
|
||||
public void handleException(RepeatContext context, Throwable throwable)
|
||||
throws RuntimeException {
|
||||
|
||||
for (Iterator iter = throwables.iterator(); iter.hasNext();) {
|
||||
Throwable throwable = (Throwable) iter.next();
|
||||
Object key = exceptionClassifier.classify(throwable);
|
||||
RepeatContextCounter counter = getCounter(context, key);
|
||||
counter.increment();
|
||||
int count = counter.getCount();
|
||||
Integer threshold = (Integer) thresholds.get(key);
|
||||
if (threshold == null || count > threshold.intValue()) {
|
||||
DefaultExceptionHandler.rethrow(throwable);
|
||||
}
|
||||
Object key = exceptionClassifier.classify(throwable);
|
||||
RepeatContextCounter counter = getCounter(context, key);
|
||||
counter.increment();
|
||||
int count = counter.getCount();
|
||||
Integer threshold = (Integer) thresholds.get(key);
|
||||
if (threshold == null || count > threshold.intValue()) {
|
||||
DefaultExceptionHandler.rethrow(throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private RepeatContextCounter getCounter(RepeatContext context, Object key) {
|
||||
String attribute = RethrowOnThresholdExceptionHandler.class + "." + key.toString();
|
||||
String attribute = RethrowOnThresholdExceptionHandler.class + "."
|
||||
+ key.toString();
|
||||
// Creates a new counter and stores it in the correct context:
|
||||
return new RepeatContextCounter(context, attribute, useParent);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.springframework.batch.common.ExceptionClassifierSupport;
|
||||
@@ -78,11 +77,11 @@ public class SimpleLimitExceptionHandler implements ExceptionHandler {
|
||||
* @see #setType(Class)
|
||||
* @see #setLimit(int)
|
||||
*
|
||||
* @see org.springframework.batch.repeat.exception.handler.ExceptionHandler#handleExceptions(org.springframework.batch.repeat.RepeatContext,
|
||||
* java.util.Collection)
|
||||
* @see org.springframework.batch.repeat.exception.handler.ExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext,
|
||||
* Throwable)
|
||||
*/
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) throws RuntimeException {
|
||||
delegate.handleExceptions(context, throwables);
|
||||
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
|
||||
delegate.handleException(context, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
package org.springframework.batch.repeat.support;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.RepeatInterceptor;
|
||||
import org.springframework.batch.repeat.RepeatOperations;
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.exception.RepeatException;
|
||||
import org.springframework.batch.repeat.exception.handler.DefaultExceptionHandler;
|
||||
import org.springframework.batch.repeat.exception.handler.ExceptionHandler;
|
||||
import org.springframework.batch.repeat.policy.DefaultResultCompletionPolicy;
|
||||
@@ -87,7 +87,8 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* @see DefaultExceptionHandler
|
||||
* @see #setCompletionPolicy(CompletionPolicy)
|
||||
*
|
||||
* @param exceptionHandler the {@link ExceptionHandler} to use.
|
||||
* @param exceptionHandler
|
||||
* the {@link ExceptionHandler} to use.
|
||||
*/
|
||||
public void setExceptionHandler(ExceptionHandler exceptionHandler) {
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
@@ -102,8 +103,10 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
*
|
||||
* @see #setExceptionHandler(ExceptionHandler)
|
||||
*
|
||||
* @param terminationPolicy a TerminationPolicy.
|
||||
* @throws IllegalArgumentException if the argument is null
|
||||
* @param terminationPolicy
|
||||
* a TerminationPolicy.
|
||||
* @throws IllegalArgumentException
|
||||
* if the argument is null
|
||||
*/
|
||||
public void setCompletionPolicy(CompletionPolicy terminationPolicy) {
|
||||
Assert.notNull(terminationPolicy);
|
||||
@@ -126,8 +129,7 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
// This works with an asynchronous TaskExecutor: the
|
||||
// interceptors have to wait for the child processes.
|
||||
result = executeInternal(callback);
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
RepeatSynchronizationManager.clear();
|
||||
if (outer != null) {
|
||||
RepeatSynchronizationManager.register(outer);
|
||||
@@ -141,10 +143,11 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* Internal convenience method to loop over interceptors and batch
|
||||
* callbacks.
|
||||
*
|
||||
* @param callback the callback to process each element of the loop.
|
||||
* @param callback
|
||||
* the callback to process each element of the loop.
|
||||
*
|
||||
* @return the aggregate of {@link ContinuationPolicy#canContinue(Object)}
|
||||
* for all the results from the callback.
|
||||
* for all the results from the callback.
|
||||
*
|
||||
*/
|
||||
private ExitStatus executeInternal(final RepeatCallback callback) {
|
||||
@@ -190,26 +193,41 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
// Check that we are still running...
|
||||
if (running) {
|
||||
|
||||
logger.debug("Batch operation about to start at count=" + context.getStartedCount());
|
||||
logger.debug("Batch operation about to start at count="
|
||||
+ context.getStartedCount());
|
||||
|
||||
Object value = getNextResult(context, callback, state);
|
||||
try {
|
||||
|
||||
// Save a throwable for later...
|
||||
// TODO: hide this in internal state?
|
||||
if (value instanceof Throwable) {
|
||||
throwables.add(value);
|
||||
}
|
||||
result = getNextResult(context, callback, state);
|
||||
executeAfterInterceptors(context, result);
|
||||
|
||||
executeAfterInterceptors(context, value);
|
||||
// An exception alone is not sufficient grounds for not
|
||||
// continuing
|
||||
|
||||
if(value instanceof ExitStatus){
|
||||
result = (ExitStatus)value;
|
||||
} catch (Throwable throwable) {
|
||||
|
||||
// An exception alone is not sufficient grounds for not
|
||||
// continuing
|
||||
|
||||
try {
|
||||
|
||||
for (int i = interceptors.length; i-- > 0;) {
|
||||
RepeatInterceptor interceptor = interceptors[i];
|
||||
interceptor.onError(context, throwable);
|
||||
// This is not an error - only log at debug
|
||||
// level.
|
||||
logger.debug("Exception intercepted ("
|
||||
+ (i + 1) + " of "
|
||||
+ interceptors.length + ")", throwable);
|
||||
}
|
||||
|
||||
exceptionHandler.handleException(context,
|
||||
throwable);
|
||||
|
||||
} catch (Throwable handled) {
|
||||
throwables.add(handled);
|
||||
}
|
||||
}
|
||||
|
||||
// N.B. the order may be important here:
|
||||
if (isComplete(context, value) || isMarkedComplete(context)) {
|
||||
if (isComplete(context, result) || isMarkedComplete(context) || !throwables.isEmpty()) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
@@ -230,32 +248,23 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
finally {
|
||||
|
||||
try {
|
||||
for (Iterator iter = throwables.iterator(); iter.hasNext();) {
|
||||
Throwable t = (Throwable) iter.next();
|
||||
for (int i = interceptors.length; i-- > 0;) {
|
||||
RepeatInterceptor interceptor = interceptors[i];
|
||||
interceptor.onError(context, t);
|
||||
// This is not an error - only log at debug level.
|
||||
logger.debug("Exception intercepted (" + (i + 1) + " of " + interceptors.length + ")", t);
|
||||
}
|
||||
}
|
||||
|
||||
if (!throwables.isEmpty()) {
|
||||
exceptionHandler.handleExceptions(context, throwables);
|
||||
rethrow((Throwable) throwables.iterator().next());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
|
||||
} finally {
|
||||
|
||||
try {
|
||||
for (int i = interceptors.length; i-- > 0;) {
|
||||
RepeatInterceptor interceptor = interceptors[i];
|
||||
interceptor.close(context);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
// TODO: extend this to the completion policy?
|
||||
context.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -264,6 +273,17 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param next
|
||||
* @return
|
||||
*/
|
||||
private static Exception rethrow(Throwable next) throws RuntimeException {
|
||||
if (next instanceof RuntimeException) {
|
||||
throw (RuntimeException) next;
|
||||
};
|
||||
throw new RepeatException("Rethrowing exception that is no RuntimeException.", next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an internal state object that is used to store data needed
|
||||
* internally in the scope of an iteration. Used by subclasses to manage the
|
||||
@@ -271,7 +291,8 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* an accumulation of Throwable instances for processing at the end of the
|
||||
* batch.
|
||||
*
|
||||
* @param context the current {@link RepeatContext}
|
||||
* @param context
|
||||
* the current {@link RepeatContext}
|
||||
* @return a {@link RepeatInternalState} instance.
|
||||
*/
|
||||
protected RepeatInternalState createInternalState(RepeatContext context) {
|
||||
@@ -282,21 +303,23 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* Get the next completed result, possibly executing several callbacks until
|
||||
* one finally finishes.
|
||||
*
|
||||
* @param context current BatchContext.
|
||||
* @param callback the callback to execute.
|
||||
* @param state maintained by the implementation.
|
||||
* @return a finished result (possibly a Throwable instance if there is an
|
||||
* error).
|
||||
* @param context
|
||||
* current BatchContext.
|
||||
* @param callback
|
||||
* the callback to execute.
|
||||
* @param state
|
||||
* maintained by the implementation.
|
||||
* @return a finished result.
|
||||
*
|
||||
* @see {@link #isComplete(RepeatContext)}
|
||||
*/
|
||||
protected Object getNextResult(RepeatContext context, RepeatCallback callback, RepeatInternalState state) {
|
||||
protected ExitStatus getNextResult(RepeatContext context,
|
||||
RepeatCallback callback, RepeatInternalState state) throws Throwable {
|
||||
try {
|
||||
update(context);
|
||||
return callback.doInIteration(context);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
return t;
|
||||
} catch (Throwable t) {
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,9 +327,10 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* If necessary, wait for results to come back from remote or concurrent
|
||||
* processes. By default does nothing and returns true.
|
||||
*
|
||||
* @param state the internal state.
|
||||
* @param state
|
||||
* the internal state.
|
||||
* @return true if {@link #canContinue(Object)} is true for all results
|
||||
* retrieved.
|
||||
* retrieved.
|
||||
*/
|
||||
protected boolean waitForResults(RepeatInternalState state) {
|
||||
// no-op by default
|
||||
@@ -314,17 +338,14 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check return value from batch operation. It's either RepeatStatus or
|
||||
* Throwable at this point, so we just check the type and continue as
|
||||
* expected.
|
||||
* @param value the last callback result.
|
||||
* @return true if the value is Throwable or RepeatStatus.CONTINUABLE.
|
||||
* Check return value from batch operation.
|
||||
*
|
||||
* @param value
|
||||
* the last callback result.
|
||||
* @return true if the value is {@link ExitStatus#CONTINUABLE}.
|
||||
*/
|
||||
protected final boolean canContinue(Object value) {
|
||||
if (value instanceof ExitStatus) {
|
||||
return ((ExitStatus) value).isContinuable();
|
||||
}
|
||||
return true; // it's an exception
|
||||
protected final boolean canContinue(ExitStatus value) {
|
||||
return ((ExitStatus) value).isContinuable();
|
||||
}
|
||||
|
||||
private boolean isMarkedComplete(RepeatContext context) {
|
||||
@@ -342,16 +363,20 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
/**
|
||||
* Convenience method to execute after interceptors on a callback result.
|
||||
*
|
||||
* @param context the current batch context.
|
||||
* @param value the result of the callback to process.
|
||||
* @param context
|
||||
* the current batch context.
|
||||
* @param value
|
||||
* the result of the callback to process.
|
||||
*/
|
||||
protected void executeAfterInterceptors(final RepeatContext context, Object value) {
|
||||
protected void executeAfterInterceptors(final RepeatContext context,
|
||||
Object value) {
|
||||
|
||||
// Don't re-throw exceptions here: let the exception handler deal with
|
||||
// that...
|
||||
|
||||
if (value != null
|
||||
&& ((value instanceof ExitStatus) && ((ExitStatus) value).isContinuable() || (value instanceof Throwable))) {
|
||||
&& ((value instanceof ExitStatus)
|
||||
&& ((ExitStatus) value).isContinuable() || (value instanceof Throwable))) {
|
||||
for (int i = interceptors.length; i-- > 0;) {
|
||||
RepeatInterceptor interceptor = interceptors[i];
|
||||
interceptor.after(context, value);
|
||||
@@ -365,12 +390,13 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* Delegate to the {@link CompletionPolicy}.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(RepeatContext,
|
||||
* Object)
|
||||
* Object)
|
||||
*/
|
||||
public boolean isComplete(RepeatContext context, Object result) {
|
||||
boolean complete = completionPolicy.isComplete(context, result);
|
||||
if (complete) {
|
||||
logger.debug("Batch is complete according to policy and result value.");
|
||||
logger
|
||||
.debug("Batch is complete according to policy and result value.");
|
||||
}
|
||||
return complete;
|
||||
}
|
||||
@@ -383,7 +409,8 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
public boolean isComplete(RepeatContext context) {
|
||||
boolean complete = completionPolicy.isComplete(context);
|
||||
if (complete) {
|
||||
logger.debug("Batch is complete according to policy alone not including result.");
|
||||
logger
|
||||
.debug("Batch is complete according to policy alone not including result.");
|
||||
}
|
||||
return complete;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.batch.repeat.support;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.RepeatOperations;
|
||||
@@ -68,8 +69,10 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
/**
|
||||
* Setter for task executor to be used to run the individual item callbacks.
|
||||
*
|
||||
* @param taskExecutor a TaskExecutor
|
||||
* @throws IllegalArgumentException if the argument is null
|
||||
* @param taskExecutor
|
||||
* a TaskExecutor
|
||||
* @throws IllegalArgumentException
|
||||
* if the argument is null
|
||||
*/
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
Assert.notNull(taskExecutor);
|
||||
@@ -84,10 +87,12 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
* method so there is no need to synchronize access.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.support.AbstracBatchemplate#getNextResult(org.springframework.batch.item.RepeatContext,
|
||||
* org.springframework.batch.repeat.RepeatCallback,
|
||||
* org.springframework.batch.TerminationContext, java.util.List)
|
||||
* org.springframework.batch.repeat.RepeatCallback,
|
||||
* org.springframework.batch.TerminationContext, java.util.List)
|
||||
*/
|
||||
protected Object getNextResult(RepeatContext context, RepeatCallback callback, RepeatInternalState state) {
|
||||
protected ExitStatus getNextResult(RepeatContext context,
|
||||
RepeatCallback callback, RepeatInternalState state)
|
||||
throws Throwable {
|
||||
|
||||
ExecutingRunnable runnable = null;
|
||||
|
||||
@@ -121,12 +126,14 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
Object result;
|
||||
try {
|
||||
result = queue.take().getResult();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
result = e;
|
||||
throw e;
|
||||
}
|
||||
return result;
|
||||
if (result instanceof Throwable) {
|
||||
throw (Throwable) result;
|
||||
}
|
||||
return (ExitStatus) result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,22 +159,22 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
Object value;
|
||||
try {
|
||||
value = future.getResult();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: cancel batch?
|
||||
Thread.currentThread().interrupt();
|
||||
value = e;
|
||||
}
|
||||
if (value instanceof Throwable) {
|
||||
state.getThrowables().add(value);
|
||||
} else {
|
||||
result = result && canContinue((ExitStatus) value);
|
||||
executeAfterInterceptors(future.getContext(), value);
|
||||
}
|
||||
|
||||
executeAfterInterceptors(future.getContext(), value);
|
||||
result = result && canContinue(value);
|
||||
|
||||
}
|
||||
|
||||
Assert.state(futures.isEmpty(), "Future results should be empty at end of batch.");
|
||||
Assert.state(futures.isEmpty(),
|
||||
"Future results should be empty at end of batch.");
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -192,7 +199,8 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
|
||||
Object result;
|
||||
|
||||
public ExecutingRunnable(RepeatCallback callback, RepeatContext context, ResultQueue queue) {
|
||||
public ExecutingRunnable(RepeatCallback callback,
|
||||
RepeatContext context, ResultQueue queue) {
|
||||
|
||||
super();
|
||||
|
||||
@@ -216,22 +224,19 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
public void run() {
|
||||
try {
|
||||
result = callback.doInIteration(context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
result = e;
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
queue.put(this);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should we support cancellations?
|
||||
|
||||
/**
|
||||
* Get the result - never blocks because the queue manages waiting for
|
||||
* the task to finish.
|
||||
*
|
||||
* @throws InterruptedException if the thread is interrupted.
|
||||
* @throws InterruptedException
|
||||
* if the thread is interrupted.
|
||||
*/
|
||||
public Object getResult() throws InterruptedException {
|
||||
return result;
|
||||
@@ -291,10 +296,10 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
synchronized (lock) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RepeatException("InterruptedException waiting for to acquire lock on input.");
|
||||
throw new RepeatException(
|
||||
"InterruptedException waiting for to acquire lock on input.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,10 +319,10 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
// Decrement the counter only when the result is collected.
|
||||
count--;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RepeatException("Interrupted while waiting for result.");
|
||||
throw new RepeatException(
|
||||
"Interrupted while waiting for result.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -338,14 +343,15 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
* until it is ready.
|
||||
*
|
||||
* @return the result.
|
||||
* @throws InterruptedException if the thread is interrupted while
|
||||
* waiting for the result.
|
||||
* @throws InterruptedException
|
||||
* if the thread is interrupted while waiting for the
|
||||
* result.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
Object getResult() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Get the context in which the result evaluation is execututing.
|
||||
* Get the context in which the result evaluation is executing.
|
||||
*
|
||||
* @return the context of the result evaluation.
|
||||
*/
|
||||
@@ -361,7 +367,8 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
* N.B. when used with a thread pooled {@link TaskExecutor} it doesn't make
|
||||
* sense for the throttle limit to be less than the thread pool size.
|
||||
*
|
||||
* @param throttleLimit the throttleLimit to set.
|
||||
* @param throttleLimit
|
||||
* the throttleLimit to set.
|
||||
*/
|
||||
public void setThrottleLimit(int throttleLimit) {
|
||||
this.throttleLimit = throttleLimit;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
public class SpringBatchTests extends TestCase {
|
||||
|
||||
public void testPlaceholder() throws Exception {
|
||||
System.err.println("This is just a placeholder...keep an eye out for the real code coming soon.");
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,6 @@
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -31,7 +29,7 @@ public class CompositeExceptionHandlerTests extends TestCase {
|
||||
|
||||
public void testNewHandler() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(null, Collections.singleton(new RuntimeException()));
|
||||
handler.handleException(null, new RuntimeException());
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
fail("Unexpected RuntimeException");
|
||||
@@ -42,17 +40,17 @@ public class CompositeExceptionHandlerTests extends TestCase {
|
||||
final List list = new ArrayList();
|
||||
handler.setHandlers(new ExceptionHandler[] {
|
||||
new ExceptionHandler() {
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) {
|
||||
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
|
||||
list.add("1");
|
||||
}
|
||||
},
|
||||
new ExceptionHandler() {
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) {
|
||||
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
|
||||
list.add("2");
|
||||
}
|
||||
}
|
||||
});
|
||||
handler.handleExceptions(null, Collections.singleton(new RuntimeException()));
|
||||
handler.handleException(null, new RuntimeException());
|
||||
assertEquals(2, list.size());
|
||||
assertEquals("1", list.get(0));
|
||||
assertEquals("2", list.get(1));
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
@@ -30,7 +28,7 @@ public class DefaultExceptionHandlerTests extends TestCase {
|
||||
|
||||
public void testRuntimeException() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
fail("Expected RuntimeException");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals("Foo", e.getMessage());
|
||||
@@ -39,7 +37,7 @@ public class DefaultExceptionHandlerTests extends TestCase {
|
||||
|
||||
public void testError() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new Error("Foo")));
|
||||
handler.handleException(context, new Error("Foo"));
|
||||
fail("Expected BatchException");
|
||||
} catch (RepeatException e) {
|
||||
assertEquals("Foo", e.getCause().getMessage());
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -47,7 +46,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
|
||||
|
||||
public void testRuntimeException() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
fail("Expected RuntimeException");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals("Foo", e.getMessage());
|
||||
@@ -56,7 +55,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
|
||||
|
||||
public void testError() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new Error("Foo")));
|
||||
handler.handleException(context, new Error("Foo"));
|
||||
fail("Expected BatchException");
|
||||
} catch (RepeatException e) {
|
||||
assertEquals("Foo", e.getCause().getMessage());
|
||||
@@ -70,7 +69,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
|
||||
}
|
||||
});
|
||||
// No exception...
|
||||
handler.handleExceptions(context, Collections.singleton(new Error("Foo")));
|
||||
handler.handleException(context, new Error("Foo"));
|
||||
assertNotNull(writer.toString());
|
||||
}
|
||||
|
||||
@@ -81,7 +80,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
|
||||
}
|
||||
});
|
||||
// No exception...
|
||||
handler.handleExceptions(context, Collections.singleton(new Error("Foo")));
|
||||
handler.handleException(context, new Error("Foo"));
|
||||
assertNotNull(writer.toString());
|
||||
}
|
||||
|
||||
@@ -92,7 +91,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
|
||||
}
|
||||
});
|
||||
// No exception...
|
||||
handler.handleExceptions(context, Collections.singleton(new Error("Foo")));
|
||||
handler.handleException(context, new Error("Foo"));
|
||||
assertNotNull(writer.toString());
|
||||
}
|
||||
|
||||
@@ -103,7 +102,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
|
||||
}
|
||||
});
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new Error("Foo")));
|
||||
handler.handleException(context, new Error("Foo"));
|
||||
fail("Expected IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
assertTrue(e.getMessage().toLowerCase().indexOf("unclassified")>=0);
|
||||
|
||||
@@ -34,7 +34,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
|
||||
|
||||
public void testRuntimeException() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
fail("Expected RuntimeException");
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals("Foo", e.getMessage());
|
||||
@@ -43,7 +43,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
|
||||
|
||||
public void testError() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new Error("Foo")));
|
||||
handler.handleException(context, new Error("Foo"));
|
||||
fail("Expected BatchException");
|
||||
} catch (RepeatException e) {
|
||||
assertEquals("Foo", e.getCause().getMessage());
|
||||
@@ -58,7 +58,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
|
||||
});
|
||||
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(1)));
|
||||
// No exception...
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
RepeatContextCounter counter = new RepeatContextCounter(context, RethrowOnThresholdExceptionHandler.class + ".RuntimeException");
|
||||
assertNotNull(counter);
|
||||
assertEquals(1, counter.getCount());
|
||||
@@ -72,9 +72,9 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
|
||||
});
|
||||
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(1)));
|
||||
// No exception...
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
fail("Expected RuntimeException");
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
@@ -100,11 +100,11 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
|
||||
});
|
||||
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(1)));
|
||||
// No exception...
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
context = new RepeatContextSupport(parent);
|
||||
try {
|
||||
// No exception again - context is changed...
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
fail("Unexpected Error");
|
||||
@@ -120,10 +120,10 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
|
||||
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(1)));
|
||||
handler.setUseParent(true);
|
||||
// No exception...
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
context = new RepeatContextSupport(parent);
|
||||
try {
|
||||
handler.handleExceptions(context, Collections.singleton(new RuntimeException("Foo")));
|
||||
handler.handleException(context, new RuntimeException("Foo"));
|
||||
fail("Expected Error");
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
package org.springframework.batch.repeat.exception.handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.io.exception.TransactionInvalidException;
|
||||
import org.springframework.batch.repeat.context.RepeatContextSupport;
|
||||
import org.springframework.batch.repeat.exception.handler.SimpleLimitExceptionHandler;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SimpleLimitExceptionHandler}
|
||||
@@ -36,144 +35,195 @@ public class SimpleLimitExceptionHandlerTests extends TestCase {
|
||||
|
||||
// object under test
|
||||
private SimpleLimitExceptionHandler handler = new SimpleLimitExceptionHandler();
|
||||
|
||||
|
||||
public void testInitializeWithNullContext() throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(null, Collections.singleton(new RuntimeException("foo")));
|
||||
handler.handleException(null, new RuntimeException("foo"));
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testInitializeWithNullContextAndEmptyList() throws Exception {
|
||||
|
||||
public void testInitializeWithNullContextAndNullException()
|
||||
throws Exception {
|
||||
try {
|
||||
handler.handleExceptions(null, Collections.EMPTY_LIST);
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected IllegalArgumentException");
|
||||
handler.handleException(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
// expected;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Other than TransactionInvalidException should be rethrown, ignoring the exception limit.
|
||||
* Other than TransactionInvalidException should be rethrown, ignoring the
|
||||
* exception limit.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testNormalExceptionThrown() {
|
||||
List throwables = Collections.singletonList(new RuntimeException("foo"));
|
||||
|
||||
public void testNormalExceptionThrown() throws Exception {
|
||||
Throwable throwable = new RuntimeException("foo");
|
||||
|
||||
final int MORE_THAN_ZERO = 1;
|
||||
handler.setLimit(MORE_THAN_ZERO);
|
||||
|
||||
try{
|
||||
handler.handleExceptions(new RepeatContextSupport(null), throwables);
|
||||
|
||||
try {
|
||||
handler.handleException(new RepeatContextSupport(null), throwable);
|
||||
fail("Exception swallowed.");
|
||||
} catch (RuntimeException expected) {
|
||||
assertTrue("Exception is rethrown, ignoring the exception limit",true);
|
||||
assertSame(throwables.get(0), expected);
|
||||
assertTrue("Exception is rethrown, ignoring the exception limit",
|
||||
true);
|
||||
assertSame(expected, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TransactionInvalidException should only be rethrown below the exception limit.
|
||||
* TransactionInvalidException should only be rethrown below the exception
|
||||
* limit.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testLimitedExceptionTypeNotThrown() {
|
||||
List throwables = Collections.singletonList(new RuntimeException("foo"));
|
||||
|
||||
public void testLimitedExceptionTypeNotThrown() throws Exception {
|
||||
final int MORE_THAN_ZERO = 1;
|
||||
handler.setLimit(MORE_THAN_ZERO);
|
||||
handler.setType(RuntimeException.class);
|
||||
|
||||
try{
|
||||
handler.handleExceptions(new RepeatContextSupport(null), throwables);
|
||||
|
||||
try {
|
||||
handler.handleException(new RepeatContextSupport(null),
|
||||
new RuntimeException("foo"));
|
||||
} catch (RuntimeException expected) {
|
||||
fail("Unexpected exception.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TransactionInvalidException should only be rethrown below the exception limit.
|
||||
* TransactionInvalidException should only be rethrown below the exception
|
||||
* limit.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testLimitedExceptionNotThrownFromSiblings() {
|
||||
List throwables = Collections.singletonList(new RuntimeException("foo"));
|
||||
|
||||
public void testLimitedExceptionNotThrownFromSiblings() throws Exception {
|
||||
Throwable throwable = new RuntimeException("foo");
|
||||
|
||||
final int MORE_THAN_ZERO = 1;
|
||||
handler.setLimit(MORE_THAN_ZERO);
|
||||
handler.setType(RuntimeException.class);
|
||||
|
||||
|
||||
RepeatContextSupport parent = new RepeatContextSupport(null);
|
||||
|
||||
try{
|
||||
try {
|
||||
RepeatContextSupport context = new RepeatContextSupport(parent);
|
||||
handler.handleExceptions(context, throwables);
|
||||
handler.handleException(context, throwable);
|
||||
context = new RepeatContextSupport(parent);
|
||||
handler.handleExceptions(context, throwables);
|
||||
handler.handleException(context, throwable);
|
||||
} catch (RuntimeException expected) {
|
||||
fail("Unexpected exception.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TransactionInvalidException should only be rethrown below the exception limit.
|
||||
* TransactionInvalidException should only be rethrown below the exception
|
||||
* limit.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testLimitedExceptionThrownFromSiblingsWhenUsingParent() {
|
||||
List throwables = Collections.singletonList(new RuntimeException("foo"));
|
||||
|
||||
public void testLimitedExceptionThrownFromSiblingsWhenUsingParent()
|
||||
throws Exception {
|
||||
Throwable throwable = new RuntimeException("foo");
|
||||
|
||||
final int MORE_THAN_ZERO = 1;
|
||||
handler.setLimit(MORE_THAN_ZERO);
|
||||
handler.setType(RuntimeException.class);
|
||||
handler.setUseParent(true);
|
||||
|
||||
|
||||
RepeatContextSupport parent = new RepeatContextSupport(null);
|
||||
|
||||
try{
|
||||
try {
|
||||
RepeatContextSupport context = new RepeatContextSupport(parent);
|
||||
handler.handleExceptions(context, throwables);
|
||||
handler.handleException(context, throwable);
|
||||
context = new RepeatContextSupport(parent);
|
||||
handler.handleExceptions(context, throwables);
|
||||
handler.handleException(context, throwable);
|
||||
fail("Expected exception.");
|
||||
} catch (RuntimeException expected) {
|
||||
assertSame(throwables.get(0), expected);
|
||||
assertSame(throwable, expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TransactionInvalidExceptions are swallowed until the exception limit is exceeded.
|
||||
* After the limit is exceeded exceptions are rethrown as BatchCriticalExceptions
|
||||
* TransactionInvalidExceptions are swallowed until the exception limit is
|
||||
* exceeded. After the limit is exceeded exceptions are rethrown as
|
||||
* BatchCriticalExceptions
|
||||
*/
|
||||
public void testExceptionThrownAboveLimit() {
|
||||
|
||||
public void testExceptionNotThrownBelowLimit() throws Exception {
|
||||
|
||||
final int EXCEPTION_LIMIT = 3;
|
||||
handler.setLimit(EXCEPTION_LIMIT);
|
||||
|
||||
List throwables = new ArrayList() {{
|
||||
for (int i = 0; i < (EXCEPTION_LIMIT); i++) {
|
||||
add(new TransactionInvalidException("below exception limit"));
|
||||
|
||||
List throwables = new ArrayList() {
|
||||
{
|
||||
for (int i = 0; i < (EXCEPTION_LIMIT); i++) {
|
||||
add(new TransactionInvalidException("below exception limit"));
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
};
|
||||
|
||||
RepeatContextSupport context = new RepeatContextSupport(null);
|
||||
|
||||
try {
|
||||
handler.handleExceptions(context, throwables);
|
||||
assertTrue("exceptions up to limit are swallowed", true);
|
||||
for (Iterator iterator = throwables.iterator(); iterator.hasNext();) {
|
||||
Throwable throwable = (Throwable) iterator.next();
|
||||
|
||||
handler.handleException(context, throwable);
|
||||
assertTrue("exceptions up to limit are swallowed", true);
|
||||
|
||||
}
|
||||
} catch (RuntimeException unexpected) {
|
||||
fail("exception rethrown although exception limit was not exceeded");
|
||||
}
|
||||
|
||||
|
||||
throwables = new ArrayList() {{
|
||||
add(new TransactionInvalidException("above exception limit"));
|
||||
}};
|
||||
|
||||
// after reaching the limit, behaviour should be idempotent
|
||||
final int ARBITRARY_REPEAT_COUNT = 2;
|
||||
for (int i = 0; i < ARBITRARY_REPEAT_COUNT; i++) {
|
||||
try {
|
||||
handler.handleExceptions(context, throwables);
|
||||
fail("exception above exception limit swallowed");
|
||||
} catch (TransactionInvalidException expected) {
|
||||
assertSame(throwables.get(0), expected);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TransactionInvalidExceptions are swallowed until the exception limit is
|
||||
* exceeded. After the limit is exceeded exceptions are rethrown as
|
||||
* BatchCriticalExceptions
|
||||
*/
|
||||
public void testExceptionThrownAboveLimit() throws Exception {
|
||||
|
||||
final int EXCEPTION_LIMIT = 3;
|
||||
handler.setLimit(EXCEPTION_LIMIT);
|
||||
|
||||
List throwables = new ArrayList() {
|
||||
{
|
||||
for (int i = 0; i < (EXCEPTION_LIMIT); i++) {
|
||||
add(new TransactionInvalidException("below exception limit"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
throwables
|
||||
.add(new TransactionInvalidException("above exception limit"));
|
||||
|
||||
RepeatContextSupport context = new RepeatContextSupport(null);
|
||||
|
||||
try {
|
||||
for (Iterator iterator = throwables.iterator(); iterator.hasNext();) {
|
||||
Throwable throwable = (Throwable) iterator.next();
|
||||
|
||||
handler.handleException(context, throwable);
|
||||
assertTrue("exceptions up to limit are swallowed", true);
|
||||
|
||||
}
|
||||
} catch (TransactionInvalidException expected) {
|
||||
assertEquals("above exception limit", expected.getMessage());
|
||||
}
|
||||
|
||||
// after reaching the limit, behaviour should be idempotent
|
||||
try {
|
||||
handler.handleException(context, new RuntimeException("foo"));
|
||||
assertTrue("exceptions up to limit are swallowed", true);
|
||||
|
||||
} catch (RuntimeException expected) {
|
||||
assertEquals("foo", expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.RepeatInterceptor;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.support.RepeatTemplate;
|
||||
import org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
@@ -221,8 +221,8 @@ public class RepeatInterceptorTests extends TestCase {
|
||||
// expected
|
||||
}
|
||||
assertEquals(0, count);
|
||||
// The after is executed, then the on error...
|
||||
assertEquals("[1, 2]", calls.toString());
|
||||
// The after is not executed, if there is an error...
|
||||
assertEquals("[2]", calls.toString());
|
||||
}
|
||||
|
||||
public void testAsynchronousOnErrorInterceptorsPrecedence() throws Exception {
|
||||
@@ -250,10 +250,14 @@ public class RepeatInterceptorTests extends TestCase {
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
// expected
|
||||
assertEquals("Bogus", e.getMessage());
|
||||
}
|
||||
assertEquals(0, count);
|
||||
// The after is executed, then the on error...
|
||||
assertEquals(calls.lastIndexOf("1") + 1, calls.indexOf("2"));
|
||||
assertEquals(fails.size() * 2, calls.size());
|
||||
System.err.println(calls);
|
||||
// The after is not executed on error...
|
||||
assertEquals("2", calls.get(0));
|
||||
assertEquals("2", calls.get(calls.size()-1));
|
||||
assertFalse(calls.contains("1"));
|
||||
assertEquals(fails.size(), calls.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public abstract class AbstractTradeBatchTests extends TestCase {
|
||||
|
||||
Resource resource = new ClassPathResource("trades.csv", getClass());
|
||||
|
||||
protected TradeProcessor executor = new TradeProcessor();
|
||||
protected TradeProcessor processor = new TradeProcessor();
|
||||
|
||||
protected TradeItemProvider provider;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class AsynchronousRepeatTests extends AbstractTradeBatchTests {
|
||||
final String threadName = Thread.currentThread().getName();
|
||||
final Set threadNames = new HashSet();
|
||||
|
||||
final RepeatCallback callback = new ItemProviderRepeatCallback(provider, executor) {
|
||||
final RepeatCallback callback = new ItemProviderRepeatCallback(provider, processor) {
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
assertNotSame(threadName, Thread.currentThread().getName());
|
||||
threadNames.add(Thread.currentThread().getName());
|
||||
@@ -53,7 +53,7 @@ public class AsynchronousRepeatTests extends AbstractTradeBatchTests {
|
||||
template.iterate(callback);
|
||||
// Shouldn't be necessary to wait:
|
||||
// Thread.sleep(500);
|
||||
assertEquals(NUMBER_OF_ITEMS, executor.count);
|
||||
assertEquals(NUMBER_OF_ITEMS, processor.count);
|
||||
assertTrue(threadNames.size() > 1);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class AsynchronousRepeatTests extends AbstractTradeBatchTests {
|
||||
final String threadName = Thread.currentThread().getName();
|
||||
final Set threadNames = new HashSet();
|
||||
|
||||
final RepeatCallback stepCallback = new ItemProviderRepeatCallback(provider, executor) {
|
||||
final RepeatCallback stepCallback = new ItemProviderRepeatCallback(provider, processor) {
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
assertNotSame(threadName, Thread.currentThread().getName());
|
||||
threadNames.add(Thread.currentThread().getName());
|
||||
@@ -90,7 +90,7 @@ public class AsynchronousRepeatTests extends AbstractTradeBatchTests {
|
||||
jobTemplate.iterate(jobCallback);
|
||||
// Shouldn't be necessary to wait:
|
||||
// Thread.sleep(500);
|
||||
assertEquals(NUMBER_OF_ITEMS, executor.count);
|
||||
assertEquals(NUMBER_OF_ITEMS, processor.count);
|
||||
// Because of the throttling and queing internally to a TaskExecutor,
|
||||
// more than one thread wil be used - the number used is (as of writing)
|
||||
// one less than the throttle limit of the template.
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ChunkedRepeatTests extends AbstractTradeBatchTests {
|
||||
public void testChunkedBatchWithTerminationPolicy() throws Exception {
|
||||
|
||||
RepeatTemplate repeatTemplate = new RepeatTemplate();
|
||||
final RepeatCallback callback = new ItemProviderRepeatCallback(provider, executor);
|
||||
final RepeatCallback callback = new ItemProviderRepeatCallback(provider, processor);
|
||||
|
||||
final RepeatTemplate chunkTemplate = new RepeatTemplate();
|
||||
// The policy is resettable so we only have to resolve this dependency
|
||||
@@ -62,7 +62,7 @@ public class ChunkedRepeatTests extends AbstractTradeBatchTests {
|
||||
|
||||
});
|
||||
|
||||
assertEquals(NUMBER_OF_ITEMS, executor.count);
|
||||
assertEquals(NUMBER_OF_ITEMS, processor.count);
|
||||
// The chunk executes 3 times because the last one
|
||||
// returns false. We terminate the main batch when
|
||||
// we encounter a partially empty chunk.
|
||||
@@ -77,10 +77,10 @@ public class ChunkedRepeatTests extends AbstractTradeBatchTests {
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testAsynchronousChunkedBatchWithTerminationPolicy() throws Exception {
|
||||
public void testAsynchronousChunkedBatchWithCompletionPolicy() throws Exception {
|
||||
|
||||
RepeatTemplate repeatTemplate = new RepeatTemplate();
|
||||
final RepeatCallback callback = new ItemProviderRepeatCallback(provider, executor);
|
||||
final RepeatCallback callback = new ItemProviderRepeatCallback(provider, processor);
|
||||
|
||||
final TaskExecutorRepeatTemplate chunkTemplate = new TaskExecutorRepeatTemplate();
|
||||
// The policy is resettable so we only have to resolve this dependency
|
||||
@@ -97,9 +97,9 @@ public class ChunkedRepeatTests extends AbstractTradeBatchTests {
|
||||
|
||||
});
|
||||
|
||||
assertEquals(NUMBER_OF_ITEMS, executor.count);
|
||||
assertEquals(NUMBER_OF_ITEMS, processor.count);
|
||||
assertFalse(result.isContinuable());
|
||||
assertEquals(3, count);
|
||||
assertTrue("Expected at least 3 chunks but found: "+count, count>=3);
|
||||
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ public class ChunkedRepeatTests extends AbstractTradeBatchTests {
|
||||
}
|
||||
};
|
||||
chunker.reset();
|
||||
template.iterate(new ItemProviderRepeatCallback(truncated, executor) {
|
||||
template.iterate(new ItemProviderRepeatCallback(truncated, processor) {
|
||||
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
ExitStatus result = super.doInIteration(context);
|
||||
@@ -173,7 +173,7 @@ public class ChunkedRepeatTests extends AbstractTradeBatchTests {
|
||||
|
||||
}
|
||||
|
||||
assertEquals(NUMBER_OF_ITEMS, executor.count);
|
||||
assertEquals(NUMBER_OF_ITEMS, processor.count);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.batch.repeat.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
@@ -45,8 +44,8 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
}
|
||||
|
||||
public void testExecute() throws Exception {
|
||||
template.iterate(new ItemProviderRepeatCallback(provider, executor));
|
||||
assertEquals(NUMBER_OF_ITEMS, executor.count);
|
||||
template.iterate(new ItemProviderRepeatCallback(provider, processor));
|
||||
assertEquals(NUMBER_OF_ITEMS, processor.count);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,9 +57,9 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
|
||||
template.setCompletionPolicy(new SimpleCompletionPolicy(2));
|
||||
|
||||
template.iterate(new ItemProviderRepeatCallback(provider, executor));
|
||||
template.iterate(new ItemProviderRepeatCallback(provider, processor));
|
||||
|
||||
assertEquals(2, executor.count);
|
||||
assertEquals(2, processor.count);
|
||||
|
||||
}
|
||||
|
||||
@@ -168,8 +167,9 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
final List list = new ArrayList();
|
||||
|
||||
template.setExceptionHandler(new ExceptionHandler() {
|
||||
public void handleExceptions(RepeatContext context, Collection throwables) {
|
||||
list.addAll(throwables);
|
||||
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
|
||||
list.add(throwable);
|
||||
throw (RuntimeException) throwable;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -197,11 +197,11 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
*/
|
||||
public void testEarlyCompletionWithContext() throws Exception {
|
||||
|
||||
ExitStatus result = template.iterate(new ItemProviderRepeatCallback(provider, executor) {
|
||||
ExitStatus result = template.iterate(new ItemProviderRepeatCallback(provider, processor) {
|
||||
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
ExitStatus result = super.doInIteration(context);
|
||||
if (executor.count >= 2) {
|
||||
if (processor.count >= 2) {
|
||||
context.setCompleteOnly();
|
||||
// If we return null the batch will terminate anyway
|
||||
// without an exception...
|
||||
@@ -211,7 +211,7 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
});
|
||||
|
||||
// 2 items were processed before completion signalled
|
||||
assertEquals(2, executor.count);
|
||||
assertEquals(2, processor.count);
|
||||
|
||||
// Not all items processed
|
||||
assertTrue(result.isContinuable());
|
||||
@@ -225,11 +225,11 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
*/
|
||||
public void testEarlyCompletionWithContextTerminated() throws Exception {
|
||||
|
||||
ExitStatus result = template.iterate(new ItemProviderRepeatCallback(provider, executor) {
|
||||
ExitStatus result = template.iterate(new ItemProviderRepeatCallback(provider, processor) {
|
||||
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
ExitStatus result = super.doInIteration(context);
|
||||
if (executor.count >= 2) {
|
||||
if (processor.count >= 2) {
|
||||
context.setTerminateOnly();
|
||||
// If we return null the batch will terminate anyway
|
||||
// without an exception...
|
||||
@@ -239,7 +239,7 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
});
|
||||
|
||||
// 2 items were processed before completion signalled
|
||||
assertEquals(2, executor.count);
|
||||
assertEquals(2, processor.count);
|
||||
|
||||
// Not all items processed
|
||||
assertTrue(result.isContinuable());
|
||||
@@ -321,8 +321,8 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testResult() throws Exception {
|
||||
ExitStatus result = template.iterate(new ItemProviderRepeatCallback(provider, executor));
|
||||
assertEquals(NUMBER_OF_ITEMS, executor.count);
|
||||
ExitStatus result = template.iterate(new ItemProviderRepeatCallback(provider, processor));
|
||||
assertEquals(NUMBER_OF_ITEMS, processor.count);
|
||||
// We are complete - do not expect to be called again
|
||||
assertFalse(result.isContinuable());
|
||||
}
|
||||
@@ -360,13 +360,13 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
ExitStatus result = ExitStatus.FINISHED;
|
||||
|
||||
try {
|
||||
result = template.iterate(new ItemProviderRepeatCallback(provider, executor) {
|
||||
result = template.iterate(new ItemProviderRepeatCallback(provider, processor) {
|
||||
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
ExitStatus result = super.doInIteration(context);
|
||||
if (executor.count >= 2) {
|
||||
if (processor.count >= 2) {
|
||||
context.setCompleteOnly();
|
||||
throw new RuntimeException("Barf second try count=" + executor.count);
|
||||
throw new RuntimeException("Barf second try count=" + processor.count);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -379,7 +379,7 @@ public class SimpleRepeatTemplateTests extends AbstractTradeBatchTests {
|
||||
}
|
||||
|
||||
// 2 items were processed before completion signalled
|
||||
assertEquals(2, executor.count);
|
||||
assertEquals(2, processor.count);
|
||||
|
||||
System.err.println(result);
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package org.springframework.batch.sample.exception.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
@@ -11,21 +8,17 @@ import org.springframework.batch.repeat.exception.handler.ExceptionHandler;
|
||||
public class NflExceptionHandler implements ExceptionHandler {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(NflExceptionHandler.class);
|
||||
|
||||
public void handleExceptions(RepeatContext context, Collection throwables)
|
||||
.getLog(NflExceptionHandler.class);
|
||||
|
||||
public void handleException(RepeatContext context, Throwable throwable)
|
||||
throws RuntimeException {
|
||||
|
||||
Iterator it = throwables.iterator();
|
||||
while(it.hasNext()){
|
||||
Throwable t = (Throwable)it.next();
|
||||
if(!(t instanceof NumberFormatException)){
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
else{
|
||||
logger.error("Number Format Exception!", t);
|
||||
}
|
||||
|
||||
if (!(throwable instanceof NumberFormatException)) {
|
||||
throw new RuntimeException(throwable);
|
||||
} else {
|
||||
logger.error("Number Format Exception!", throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user