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:
dsyer
2007-11-20 19:19:08 +00:00
parent 419863a71e
commit 85ced3551d
11 changed files with 43 additions and 88 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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++) {

View File

@@ -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());
}
/**

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;
}
} });

View File

@@ -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);

View File

@@ -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));
}
}