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 Use ExitStatus instead of Object in CompletionPolicy.
This commit is contained in:
@@ -41,7 +41,7 @@ public interface CompletionPolicy {
|
||||
*
|
||||
* @see #isComplete(RepeatContext)
|
||||
*/
|
||||
boolean isComplete(RepeatContext context, Object result);
|
||||
boolean isComplete(RepeatContext context, ExitStatus result);
|
||||
|
||||
/**
|
||||
* Allow policy to signal completion according to internal state, without
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.batch.repeat.RepeatContext;
|
||||
* 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
|
||||
* abnormal ending - an abnormal ending would result in an
|
||||
* {@link ExceptionHandler} throwing an exception. The caller will catch and
|
||||
* rethrow it if necessary.
|
||||
*
|
||||
@@ -34,7 +34,7 @@ public interface ExceptionHandler {
|
||||
|
||||
/**
|
||||
* Deal with a Throwable during a batch. The input might be
|
||||
* RuntimeExceptions or other unchecked exceptions.
|
||||
* RuntimeException or other unchecked exceptions.
|
||||
*
|
||||
* @param context
|
||||
* the current {@link RepeatContext}. Can be used to store state
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.batch.repeat.policy;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
import org.springframework.batch.repeat.context.RepeatContextSupport;
|
||||
@@ -32,9 +33,9 @@ public class CompletionPolicySupport implements CompletionPolicy {
|
||||
* Delegate to {@link #isComplete(RepeatContext)}.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(org.springframework.batch.repeat.RepeatContext,
|
||||
* java.lang.Object)
|
||||
* ExitStatus)
|
||||
*/
|
||||
public boolean isComplete(RepeatContext context, Object result) {
|
||||
public boolean isComplete(RepeatContext context, ExitStatus result) {
|
||||
return isComplete(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.batch.repeat.policy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
import org.springframework.batch.repeat.context.RepeatContextSupport;
|
||||
@@ -47,9 +48,9 @@ public class CompositeCompletionPolicy implements CompletionPolicy {
|
||||
* This policy is complete if any of the composed policies is complete.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(org.springframework.batch.repeat.RepeatContext,
|
||||
* java.lang.Object)
|
||||
* ExitStatus)
|
||||
*/
|
||||
public boolean isComplete(RepeatContext context, Object result) {
|
||||
public boolean isComplete(RepeatContext context, ExitStatus result) {
|
||||
RepeatContext[] contexts = ((CompositeBatchContext) context).contexts;
|
||||
CompletionPolicy[] policies = ((CompositeBatchContext) context).policies;
|
||||
for (int i = 0; i < policies.length; i++) {
|
||||
|
||||
@@ -22,8 +22,8 @@ import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
/**
|
||||
* Very simple {@link CompletionPolicy} that bases its decision on the result of
|
||||
* a batch operation. If the result is Boolean.FALSE, null or an instance of
|
||||
* Throwable the batch is complete, otherwise not.
|
||||
* a batch operation. If the result is null or not continuable according to the
|
||||
* {@link ExitStatus} the batch is complete, otherwise not.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
@@ -31,15 +31,14 @@ import org.springframework.batch.repeat.ExitStatus;
|
||||
public class DefaultResultCompletionPolicy extends CompletionPolicySupport {
|
||||
|
||||
/**
|
||||
* True if the result is null, a {@link ExitStatus} indicating completion,
|
||||
* or an instance of Throwable.
|
||||
* True if the result is null, or a {@link ExitStatus} indicating
|
||||
* completion.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(org.springframework.batch.repeat.RepeatContext,
|
||||
* java.lang.Object)
|
||||
* ExitStatus)
|
||||
*/
|
||||
public boolean isComplete(RepeatContext context, Object result) {
|
||||
return (result == null || (result instanceof Throwable) || (result instanceof ExitStatus && !((ExitStatus) result)
|
||||
.isContinuable()));
|
||||
public boolean isComplete(RepeatContext context, ExitStatus result) {
|
||||
return (result == null || !result.isContinuable());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.batch.repeat.policy;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.context.RepeatContextSupport;
|
||||
import org.springframework.batch.repeat.support.RepeatTemplate;
|
||||
@@ -62,11 +63,11 @@ public class SimpleCompletionPolicy extends DefaultResultCompletionPolicy {
|
||||
* Terminate if the chunk size has been reached, or the result is null.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(RepeatContext,
|
||||
* Object)
|
||||
* ExitStatus)
|
||||
* @throws Exception (normally terminating the batch) if the result is
|
||||
* itself an exception.
|
||||
*/
|
||||
public boolean isComplete(RepeatContext context, Object result) {
|
||||
public boolean isComplete(RepeatContext context, ExitStatus result) {
|
||||
return super.isComplete(context, result) || ((SimpleTerminationContext) context).isComplete();
|
||||
}
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
// continuing
|
||||
|
||||
try {
|
||||
|
||||
|
||||
for (int i = interceptors.length; i-- > 0;) {
|
||||
RepeatInterceptor interceptor = interceptors[i];
|
||||
interceptor.onError(context, throwable);
|
||||
@@ -218,16 +218,18 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
+ interceptors.length + ")", throwable);
|
||||
}
|
||||
|
||||
exceptionHandler.handleException(context,
|
||||
throwable);
|
||||
|
||||
exceptionHandler
|
||||
.handleException(context, throwable);
|
||||
|
||||
} catch (Throwable handled) {
|
||||
throwables.add(handled);
|
||||
}
|
||||
}
|
||||
|
||||
// N.B. the order may be important here:
|
||||
if (isComplete(context, result) || isMarkedComplete(context) || !throwables.isEmpty()) {
|
||||
if (isComplete(context, result)
|
||||
|| isMarkedComplete(context)
|
||||
|| !throwables.isEmpty()) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
@@ -280,8 +282,10 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
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);
|
||||
}
|
||||
;
|
||||
throw new RepeatException(
|
||||
"Rethrowing exception that is no RuntimeException.", next);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +318,8 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* @see {@link #isComplete(RepeatContext)}
|
||||
*/
|
||||
protected ExitStatus getNextResult(RepeatContext context,
|
||||
RepeatCallback callback, RepeatInternalState state) throws Throwable {
|
||||
RepeatCallback callback, RepeatInternalState state)
|
||||
throws Throwable {
|
||||
try {
|
||||
update(context);
|
||||
return callback.doInIteration(context);
|
||||
@@ -369,14 +374,12 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* the result of the callback to process.
|
||||
*/
|
||||
protected void executeAfterInterceptors(final RepeatContext context,
|
||||
Object value) {
|
||||
ExitStatus 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))) {
|
||||
if (value != null && value.isContinuable()) {
|
||||
for (int i = interceptors.length; i-- > 0;) {
|
||||
RepeatInterceptor interceptor = interceptors[i];
|
||||
interceptor.after(context, value);
|
||||
@@ -390,9 +393,9 @@ public class RepeatTemplate implements RepeatOperations {
|
||||
* Delegate to the {@link CompletionPolicy}.
|
||||
*
|
||||
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(RepeatContext,
|
||||
* Object)
|
||||
* ExitStatus)
|
||||
*/
|
||||
public boolean isComplete(RepeatContext context, Object result) {
|
||||
public boolean isComplete(RepeatContext context, ExitStatus result) {
|
||||
boolean complete = completionPolicy.isComplete(context, result);
|
||||
if (complete) {
|
||||
logger
|
||||
|
||||
@@ -167,8 +167,9 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
|
||||
if (value instanceof Throwable) {
|
||||
state.getThrowables().add(value);
|
||||
} else {
|
||||
result = result && canContinue((ExitStatus) value);
|
||||
executeAfterInterceptors(future.getContext(), value);
|
||||
ExitStatus status = (ExitStatus) value;
|
||||
result = result && canContinue(status);
|
||||
executeAfterInterceptors(future.getContext(), status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.batch.repeat.policy;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
|
||||
@@ -58,7 +59,7 @@ public class CompositeCompletionPolicyTests extends TestCase {
|
||||
CompositeCompletionPolicy policy = new CompositeCompletionPolicy();
|
||||
policy.setPolicies(new CompletionPolicy[] { new MockCompletionPolicySupport(),
|
||||
new MockCompletionPolicySupport() {
|
||||
public boolean isComplete(RepeatContext context, Object result) {
|
||||
public boolean isComplete(RepeatContext context, ExitStatus result) {
|
||||
return true;
|
||||
}
|
||||
} });
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.batch.repeat.policy;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
|
||||
public class SimpleCompletionPolicyTests extends TestCase {
|
||||
@@ -26,7 +27,7 @@ public class SimpleCompletionPolicyTests extends TestCase {
|
||||
|
||||
RepeatContext context;
|
||||
|
||||
Object dummy = "foo";
|
||||
ExitStatus dummy = ExitStatus.CONTINUABLE;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -60,17 +61,6 @@ public class SimpleCompletionPolicyTests extends TestCase {
|
||||
assertTrue(policy.isComplete(context, null));
|
||||
}
|
||||
|
||||
public void testTerminationAfterException() throws Exception {
|
||||
policy.update(context);
|
||||
try {
|
||||
assertTrue(policy.isComplete(context, new IllegalStateException("foo")));
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
assertEquals("foo", e.getMessage());
|
||||
fail("Unxpected IllegalStateException");
|
||||
}
|
||||
}
|
||||
|
||||
public void testReset() throws Exception {
|
||||
policy.setChunkSize(2);
|
||||
policy.update(context);
|
||||
|
||||
@@ -47,46 +47,4 @@ public class TimeoutCompletionPolicyTests extends TestCase {
|
||||
assertTrue(policy.isComplete(context));
|
||||
}
|
||||
|
||||
public void testException() throws Exception {
|
||||
TimeoutTerminationPolicy policy = new TimeoutTerminationPolicy();
|
||||
RepeatContext context = policy.start(null);
|
||||
assertFalse(policy.isComplete(context));
|
||||
try {
|
||||
policy.isComplete(context, new RuntimeException("foo"));
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// expected
|
||||
assertEquals("foo", e.getMessage());
|
||||
}
|
||||
assertFalse(policy.isComplete(context));
|
||||
}
|
||||
|
||||
public void testError() throws Exception {
|
||||
TimeoutTerminationPolicy policy = new TimeoutTerminationPolicy();
|
||||
RepeatContext context = policy.start(null);
|
||||
assertFalse(policy.isComplete(context));
|
||||
try {
|
||||
policy.isComplete(context, new Error("foo"));
|
||||
}
|
||||
catch (Error e) {
|
||||
// expected
|
||||
assertEquals("foo", e.getMessage());
|
||||
}
|
||||
assertFalse(policy.isComplete(context));
|
||||
}
|
||||
|
||||
public void testThrowable() throws Exception {
|
||||
TimeoutTerminationPolicy policy = new TimeoutTerminationPolicy();
|
||||
RepeatContext context = policy.start(null);
|
||||
assertFalse(policy.isComplete(context));
|
||||
try {
|
||||
policy.isComplete(context, new Throwable("foo"));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
// expected
|
||||
assertEquals("foo", e.getCause().getMessage());
|
||||
}
|
||||
assertFalse(policy.isComplete(context));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user