RESOLVED - issue BATCH-231: Chunk completion policy

http://jira.springframework.org/browse/BATCH-231

Added RepeatOperationsStepFactoryBean.
This commit is contained in:
dsyer
2008-03-03 09:16:06 +00:00
parent a31806dedf
commit cb3c567966
13 changed files with 504 additions and 141 deletions

View File

@@ -16,7 +16,10 @@
package org.springframework.batch.repeat.support;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -47,15 +50,15 @@ import org.springframework.util.Assert;
* finish when exceptions are received. This is not the default behaviour.<br/>
*
* Clients that want to take some business action when an exception is thrown by
* the {@link RepeatCallback} can consider using a custom
* {@link RepeatListener} instead of trying to customise the
* {@link CompletionPolicy}. This is generally a friendlier interface to
* implement, and the {@link RepeatListener#after(RepeatContext, ExitStatus)}
* method is passed in the result of the callback, which would be an instance of
* {@link Throwable} if the business processing had thrown an exception. If the
* exception is not to be propagated to the caller, then a non-default
* {@link CompletionPolicy} needs to be provided as well, but that could be off
* the shelf, with the business action implemented only in the interceptor.
* the {@link RepeatCallback} can consider using a custom {@link RepeatListener}
* instead of trying to customise the {@link CompletionPolicy}. This is
* generally a friendlier interface to implement, and the
* {@link RepeatListener#after(RepeatContext, ExitStatus)} method is passed in
* the result of the callback, which would be an instance of {@link Throwable}
* if the business processing had thrown an exception. If the exception is not
* to be propagated to the caller, then a non-default {@link CompletionPolicy}
* needs to be provided as well, but that could be off the shelf, with the
* business action implemented only in the interceptor.
*
* @author Dave Syer
*
@@ -70,12 +73,25 @@ public class RepeatTemplate implements RepeatOperations {
private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();
/**
* Set the listeners for this template, registering them for callbacks at
* appropriate times in the iteration.
*
* @param listeners
*/
public void setListeners(RepeatListener[] listeners) {
this.listeners = listeners;
}
public void setListener(RepeatListener listener) {
listeners = new RepeatListener[] { listener };
/**
* Register an additional listener.
*
* @param listener
*/
public void registerListener(RepeatListener listener) {
List list = new ArrayList(Arrays.asList(listeners));
list.add(listener);
listeners = (RepeatListener[]) list.toArray(new RepeatListener[list.size()]);
}
/**
@@ -87,8 +103,7 @@ 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;
@@ -103,10 +118,8 @@ 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);
@@ -129,7 +142,8 @@ 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);
@@ -143,11 +157,10 @@ 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) {
@@ -193,15 +206,15 @@ 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());
try {
result = getNextResult(context, callback, state);
executeAfterInterceptors(context, result);
} catch (Throwable throwable) {
}
catch (Throwable throwable) {
// An exception alone is not sufficient grounds for not
// continuing
@@ -213,22 +226,19 @@ public class RepeatTemplate implements RepeatOperations {
interceptor.onError(context, throwable);
// This is not an error - only log at debug
// level.
logger.debug("Exception intercepted ("
+ (i + 1) + " of "
+ listeners.length + ")", throwable);
logger.debug("Exception intercepted (" + (i + 1) + " of " + listeners.length + ")",
throwable);
}
exceptionHandler
.handleException(context, throwable);
exceptionHandler.handleException(context, throwable);
} catch (Throwable handled) {
}
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;
}
}
@@ -254,14 +264,16 @@ public class RepeatTemplate implements RepeatOperations {
rethrow((Throwable) throwables.iterator().next());
}
} finally {
}
finally {
try {
for (int i = listeners.length; i-- > 0;) {
RepeatListener interceptor = listeners[i];
interceptor.close(context);
}
} finally {
}
finally {
// TODO: extend this to the completion policy?
context.close();
}
@@ -283,8 +295,7 @@ public class RepeatTemplate implements RepeatOperations {
throw (RuntimeException) next;
}
;
throw new RepeatException(
"Rethrowing exception that is no RuntimeException.", next);
throw new RepeatException("Rethrowing exception that is no RuntimeException.", next);
}
/**
@@ -294,8 +305,7 @@ 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) {
@@ -306,23 +316,20 @@ 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.
* @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 ExitStatus 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) {
}
catch (Throwable t) {
throw t;
}
}
@@ -331,10 +338,9 @@ 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
@@ -344,8 +350,7 @@ public class RepeatTemplate implements RepeatOperations {
/**
* Check return value from batch operation.
*
* @param value
* the last callback result.
* @param value the last callback result.
* @return true if the value is {@link ExitStatus#CONTINUABLE}.
*/
protected final boolean canContinue(ExitStatus value) {
@@ -367,13 +372,10 @@ 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,
ExitStatus value) {
protected void executeAfterInterceptors(final RepeatContext context, ExitStatus value) {
// Don't re-throw exceptions here: let the exception handler deal with
// that...
@@ -392,13 +394,12 @@ public class RepeatTemplate implements RepeatOperations {
* Delegate to the {@link CompletionPolicy}.
*
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(RepeatContext,
* ExitStatus)
* ExitStatus)
*/
public boolean isComplete(RepeatContext context, ExitStatus 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;
}
@@ -411,8 +412,7 @@ 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;
}

View File

@@ -16,6 +16,10 @@
package org.springframework.batch.retry.support;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.retry.RetryCallback;
@@ -80,12 +84,14 @@ public class RetryTemplate implements RetryOperations {
}
/**
* Setter for single listener if there is only one.
* Register an additional listener.
* @param listener
* @see #setListeners(RetryListener[])
*/
public void setListener(RetryListener listener) {
this.listeners = new RetryListener[] { listener };
public void registerListener(RetryListener listener) {
List list = new ArrayList(Arrays.asList(listeners));
list.add(listener);
listeners = (RetryListener[]) list.toArray(new RetryListener[list.size()]);
}
/**