IN PROGRESS - issue BATCH-894: RFC: move ExitStatus up into Core?

Replaced infrastrucure status with local enum and moved ExitStatus into core.   TODO: maybe get rid of continuable.
This commit is contained in:
dsyer
2008-11-07 18:40:58 +00:00
parent 9ac28ef106
commit 8b2d02f5d9
107 changed files with 475 additions and 411 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.repeat;
/**
* Interface for batch completion policies, to enable batch operations to
* strategise normal completion conditions. Stateful implementations of batch
@@ -41,7 +42,7 @@ public interface CompletionPolicy {
*
* @see #isComplete(RepeatContext)
*/
boolean isComplete(RepeatContext context, ExitStatus result);
boolean isComplete(RepeatContext context, RepeatStatus result);
/**
* Allow policy to signal completion according to internal state, without

View File

@@ -1,260 +0,0 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.repeat;
import java.io.Serializable;
import org.springframework.util.StringUtils;
/**
* Value object used to carry information about the status of a
* {@link RepeatOperations}.
*
* ExitStatus is immutable and therefore thread-safe.
*
* @author Dave Syer
*
*/
public class ExitStatus implements Serializable {
/**
* Convenient constant value representing unknown state - assumed not
* continuable.
*/
public static final ExitStatus UNKNOWN = new ExitStatus(false, "UNKNOWN");
/**
* Convenient constant value representing unfinished processing.
*/
public static final ExitStatus CONTINUABLE = new ExitStatus(true, "CONTINUABLE");
/**
* Convenient constant value representing continuable state where processing
* is still taking place, so no further action is required. Used for
* asynchronous execution scenarios where the processing is happening in
* another thread or process and the caller is not required to wait for the
* result.
*/
public static final ExitStatus EXECUTING = new ExitStatus(true, "EXECUTING");
/**
* Convenient constant value representing finished processing.
*/
public static final ExitStatus FINISHED = new ExitStatus(false, "COMPLETED");
/**
* Convenient constant value representing job that did no processing (e.g.
* because it was already complete).
*/
public static final ExitStatus NOOP = new ExitStatus(false, "NOOP");
/**
* Convenient constant value representing finished processing with an error.
*/
public static final ExitStatus FAILED = new ExitStatus(false, "FAILED");
private final boolean continuable;
private final String exitCode;
private final String exitDescription;
public ExitStatus(boolean continuable) {
this(continuable, "", "");
}
public ExitStatus(boolean continuable, String exitCode) {
this(continuable, exitCode, "");
}
public ExitStatus(boolean continuable, String exitCode, String exitDescription) {
super();
this.continuable = continuable;
this.exitCode = exitCode;
this.exitDescription = exitDescription;
}
/**
* Flag to signal that processing can continue. This is distinct from any
* flag that might indicate that a batch is complete, or terminated, since a
* batch might be only a small part of a larger whole, which is still not
* finished.
*
* @return true if processing can continue.
*/
public boolean isContinuable() {
return continuable;
}
/**
* Getter for the exit code (defaults to blank).
*
* @return the exit code.
*/
public String getExitCode() {
return exitCode;
}
/**
* Getter for the exit description (defaults to blank)
*/
public String getExitDescription() {
return exitDescription;
}
/**
* Create a new {@link ExitStatus} with a logical combination of the
* continuable flag.
*
* @param continuable true if the caller thinks it is safe to continue.
* @return a new {@link ExitStatus} with {@link #isContinuable()} the
* logical and of the current value and the argument provided.
*/
public ExitStatus and(boolean continuable) {
return new ExitStatus(this.continuable && continuable, this.exitCode, this.exitDescription);
}
/**
* Create a new {@link ExitStatus} with a logical combination of the
* continuable flag, and a concatenation of the descriptions. If either
* value has a higher severity then its exit code will be used in the
* result. In the case of equal severity, the exit code is only replaced if
* the result is continuable or the input is not continuable.<br/>
* <br/>
*
* Severity is defined by the exit code:
* <ul>
* <li>Codes beginning with NOOP have severity 1</li>
* <li>Codes beginning with FAILED have severity 2</li>
* <li>Codes beginning with UNKNOWN have severity 3</li>
* </ul>
* Others have severity 0.<br/>
*
* If the input is null just return this.
*
* @param status an {@link ExitStatus} to combine with this one.
* @return a new {@link ExitStatus} with {@link #isContinuable()} the
* logical and of the current value and the argument provided.
*/
public ExitStatus and(ExitStatus status) {
if (status == null) {
return this;
}
ExitStatus result = and(status.continuable).addExitDescription(status.exitDescription);
if (severity(status) > severity(this)) {
result = result.replaceExitCode(status.exitCode);
}
else {
if (severity(this) == severity(status) && (result.continuable || !status.continuable)) {
result = result.replaceExitCode(status.exitCode);
}
}
return result;
}
/**
* @param status
* @return
*/
private int severity(ExitStatus status) {
if (status.exitCode.startsWith(NOOP.exitCode)) {
return 0;
}
if (status.exitCode.startsWith(FAILED.exitCode)) {
return 1;
}
if (status.exitCode.startsWith(UNKNOWN.exitCode)) {
return 2;
}
return 0;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString() {
return String.format("continuable=%s;exitCode=%s;exitDescription=%s", continuable, exitCode, exitDescription);
}
/**
* Compare the fields one by one.
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
return toString().equals(obj.toString());
}
/**
* Compatible with the equals implementation.
*
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return toString().hashCode();
}
/**
* Add an exit code to an existing {@link ExitStatus}. If there is already a
* code present tit will be replaced.
*
* @param code the code to add
* @return a new {@link ExitStatus} with the same properties but a new exit
* code.
*/
public ExitStatus replaceExitCode(String code) {
return new ExitStatus(continuable, code, exitDescription);
}
/**
* Check if this status represents a running process.
*
* @return true if the exit code is "RUNNING" or "UNKNOWN"
*/
public boolean isRunning() {
return "RUNNING".equals(this.exitCode) || "UNKNOWN".equals(this.exitCode);
}
/**
* Add an exit description to an existing {@link ExitStatus}. If there is
* already a description present the two will be concatenated with a
* semicolon.
*
* @param description the description to add
* @return a new {@link ExitStatus} with the same properties but a new exit
* description
*/
public ExitStatus addExitDescription(String description) {
StringBuffer buffer = new StringBuffer();
boolean changed = StringUtils.hasText(description) && !exitDescription.equals(description);
if (StringUtils.hasText(exitDescription)) {
buffer.append(exitDescription);
if (changed) {
buffer.append("; ");
}
}
if (changed) {
buffer.append(description);
}
return new ExitStatus(continuable, exitCode, buffer.toString());
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.repeat;
/**
* Callback interface for batch operations. Many simple processes will be able
* to use off-the-shelf implementations of this interface, enabling the
@@ -35,9 +36,9 @@ public interface RepeatCallback {
* implementation of the caller.
*
* @param context the current context passed in by the caller.
* @return an {@link ExitStatus} which is continuable if there is (or may
* @return an {@link RepeatStatus} which is continuable if there is (or may
* be) more data to process.
* @throws Exception if there is a problem with the processing.
*/
ExitStatus doInIteration(RepeatContext context) throws Exception;
RepeatStatus doInIteration(RepeatContext context) throws Exception;
}

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.repeat;
/**
* Interface for listeners to the batch process. Implementers can provide
* enhance the behaviour of a batch in small cross-cutting modules. The
@@ -41,7 +42,7 @@ public interface RepeatListener {
* @param context the current batch context
* @param result the result of the callback
*/
void after(RepeatContext context, ExitStatus result);
void after(RepeatContext context, RepeatStatus result);
/**
* Called once at the start of a complete batch, before any items are

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.repeat;
/**
* The main interface providing access to batch operations. The batch client is
* the {@link RepeatCallback}, where a single item or record is processed. The
@@ -41,6 +42,6 @@ public interface RepeatOperations {
* indication of whether the {@link RepeatOperations} can continue
* processing if this method is called again.
*/
ExitStatus iterate(RepeatCallback callback) throws RepeatException;
RepeatStatus iterate(RepeatCallback callback) throws RepeatException;
}

View File

@@ -0,0 +1,25 @@
package org.springframework.batch.repeat;
public enum RepeatStatus {
UNKNOWN(true), CONTINUABLE(true), FINISHED(false);
private final boolean continuable;
private RepeatStatus(boolean continuable) {
this.continuable = continuable;
}
public static RepeatStatus continueIf(boolean continuable) {
return continuable ? CONTINUABLE : FINISHED;
}
public boolean isContinuable() {
return this == CONTINUABLE;
}
public RepeatStatus and(boolean value) {
return value && continuable ? CONTINUABLE : FINISHED;
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.batch.repeat.callback;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatOperations;
@@ -55,7 +55,7 @@ public class NestedRepeatCallback implements RepeatCallback {
*
* @see org.springframework.batch.repeat.RepeatCallback#doInIteration(RepeatContext)
*/
public ExitStatus doInIteration(RepeatContext context) throws Exception {
public RepeatStatus doInIteration(RepeatContext context) throws Exception {
return template.iterate(callback);
}
}

View File

@@ -19,7 +19,7 @@ package org.springframework.batch.repeat.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatException;
@@ -75,7 +75,7 @@ public class RepeatOperationsInterceptor implements MethodInterceptor {
try {
repeatOperations.iterate(new RepeatCallback() {
public ExitStatus doInIteration(RepeatContext context) throws Exception {
public RepeatStatus doInIteration(RepeatContext context) throws Exception {
try {
MethodInvocation clone = invocation;
@@ -89,16 +89,16 @@ public class RepeatOperationsInterceptor implements MethodInterceptor {
Object value = clone.proceed();
if (voidReturnType) {
return ExitStatus.CONTINUABLE;
return RepeatStatus.CONTINUABLE;
}
if (!isComplete(value)) {
// Save the last result
result.setValue(value);
return ExitStatus.CONTINUABLE;
return RepeatStatus.CONTINUABLE;
}
else {
result.setFinalValue(value);
return ExitStatus.FINISHED;
return RepeatStatus.FINISHED;
}
}
catch (Throwable e) {

View File

@@ -19,7 +19,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatListener;
@@ -54,7 +54,7 @@ public class CompositeRepeatListener implements RepeatListener {
/* (non-Javadoc)
* @see org.springframework.batch.repeat.RepeatListener#after(org.springframework.batch.repeat.RepeatContext, org.springframework.batch.repeat.ExitStatus)
*/
public void after(RepeatContext context, ExitStatus result) {
public void after(RepeatContext context, RepeatStatus result) {
for (RepeatListener listener : listeners) {
listener.after(context, result);
}

View File

@@ -16,7 +16,7 @@
package org.springframework.batch.repeat.listener;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatListener;
@@ -31,7 +31,7 @@ public class RepeatListenerSupport implements RepeatListener {
public void before(RepeatContext context) {
}
public void after(RepeatContext context, ExitStatus result) {
public void after(RepeatContext context, RepeatStatus result) {
}
public void close(RepeatContext context) {

View File

@@ -17,7 +17,7 @@
package org.springframework.batch.repeat.policy;
import org.springframework.batch.repeat.CompletionPolicy;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.context.RepeatContextSupport;
@@ -34,9 +34,9 @@ public class CompletionPolicySupport implements CompletionPolicy {
* delegate to {@link #isComplete(RepeatContext)}.
*
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(org.springframework.batch.repeat.RepeatContext,
* ExitStatus)
* RepeatStatus)
*/
public boolean isComplete(RepeatContext context, ExitStatus result) {
public boolean isComplete(RepeatContext context, RepeatStatus result) {
if (result != null && !result.isContinuable()) {
return true;
}

View File

@@ -20,7 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.repeat.CompletionPolicy;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.context.RepeatContextSupport;
@@ -48,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,
* ExitStatus)
* RepeatStatus)
*/
public boolean isComplete(RepeatContext context, ExitStatus result) {
public boolean isComplete(RepeatContext context, RepeatStatus result) {
RepeatContext[] contexts = ((CompositeBatchContext) context).contexts;
CompletionPolicy[] policies = ((CompositeBatchContext) context).policies;
for (int i = 0; i < policies.length; i++) {

View File

@@ -17,13 +17,13 @@
package org.springframework.batch.repeat.policy;
import org.springframework.batch.repeat.CompletionPolicy;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
/**
* Very simple {@link CompletionPolicy} that bases its decision on the result of
* a batch operation. If the result is null or not continuable according to the
* {@link ExitStatus} the batch is complete, otherwise not.
* {@link RepeatStatus} the batch is complete, otherwise not.
*
* @author Dave Syer
*
@@ -31,13 +31,13 @@ import org.springframework.batch.repeat.RepeatContext;
public class DefaultResultCompletionPolicy extends CompletionPolicySupport {
/**
* True if the result is null, or a {@link ExitStatus} indicating
* True if the result is null, or a {@link RepeatStatus} indicating
* completion.
*
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(org.springframework.batch.repeat.RepeatContext,
* ExitStatus)
* RepeatStatus)
*/
public boolean isComplete(RepeatContext context, ExitStatus result) {
public boolean isComplete(RepeatContext context, RepeatStatus result) {
return (result == null || !result.isContinuable());
}

View File

@@ -16,7 +16,7 @@
package org.springframework.batch.repeat.policy;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.context.RepeatContextSupport;
import org.springframework.batch.repeat.support.RepeatTemplate;
@@ -64,11 +64,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,
* ExitStatus)
* RepeatStatus)
* @throws RuntimeException (normally terminating the batch) if the result is
* itself an exception.
*/
public boolean isComplete(RepeatContext context, ExitStatus result) {
public boolean isComplete(RepeatContext context, RepeatStatus result) {
return super.isComplete(context, result) || ((SimpleTerminationContext) context).isComplete();
}

View File

@@ -24,7 +24,7 @@ import java.util.List;
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.RepeatStatus;
import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatException;
@@ -52,7 +52,7 @@ import org.springframework.util.Assert;
* 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
* {@link RepeatListener#after(RepeatContext, RepeatStatus)} 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}
@@ -110,7 +110,7 @@ public class RepeatTemplate implements RepeatOperations {
/**
* Setter for policy to decide when the batch is complete. The default is to
* complete normally when the callback returns a {@link ExitStatus} which is
* complete normally when the callback returns a {@link RepeatStatus} which is
* not marked as continuable, and abnormally when the callback throws an
* exception (but the decision to re-throw the exception is deferred to the
* {@link ExceptionHandler}).
@@ -132,11 +132,11 @@ public class RepeatTemplate implements RepeatOperations {
*
* @see org.springframework.batch.repeat.RepeatOperations#iterate(org.springframework.batch.repeat.RepeatCallback)
*/
public ExitStatus iterate(RepeatCallback callback) {
public RepeatStatus iterate(RepeatCallback callback) {
RepeatContext outer = RepeatSynchronizationManager.getContext();
ExitStatus result = ExitStatus.CONTINUABLE;
RepeatStatus result = RepeatStatus.CONTINUABLE;
try {
// This works with an asynchronous TaskExecutor: the
// interceptors have to wait for the child processes.
@@ -162,7 +162,7 @@ public class RepeatTemplate implements RepeatOperations {
* for all the results from the callback.
*
*/
private ExitStatus executeInternal(final RepeatCallback callback) {
private RepeatStatus executeInternal(final RepeatCallback callback) {
// Reset the termination policy if there is one...
RepeatContext context = start();
@@ -180,7 +180,7 @@ public class RepeatTemplate implements RepeatOperations {
}
// Return value, default is to allow continued processing.
ExitStatus result = ExitStatus.CONTINUABLE;
RepeatStatus result = RepeatStatus.CONTINUABLE;
RepeatInternalState state = createInternalState(context);
Collection<Throwable> throwables = state.getThrowables();
@@ -346,7 +346,7 @@ public class RepeatTemplate implements RepeatOperations {
* @see #isComplete(RepeatContext)
* @see #createInternalState(RepeatContext)
*/
protected ExitStatus getNextResult(RepeatContext context, RepeatCallback callback, RepeatInternalState state)
protected RepeatStatus getNextResult(RepeatContext context, RepeatCallback callback, RepeatInternalState state)
throws Throwable {
update(context);
return callback.doInIteration(context);
@@ -358,7 +358,7 @@ public class RepeatTemplate implements RepeatOperations {
* processes. By default does nothing and returns true.
*
* @param state the internal state.
* @return true if {@link #canContinue(ExitStatus)} is true for all results
* @return true if {@link #canContinue(RepeatStatus)} is true for all results
* retrieved.
*/
protected boolean waitForResults(RepeatInternalState state) {
@@ -370,10 +370,10 @@ public class RepeatTemplate implements RepeatOperations {
* Check return value from batch operation.
*
* @param value the last callback result.
* @return true if the value is {@link ExitStatus#CONTINUABLE}.
* @return true if the value is {@link RepeatStatus#CONTINUABLE}.
*/
protected final boolean canContinue(ExitStatus value) {
return ((ExitStatus) value).isContinuable();
protected final boolean canContinue(RepeatStatus value) {
return ((RepeatStatus) value).isContinuable();
}
private boolean isMarkedComplete(RepeatContext context) {
@@ -394,7 +394,7 @@ public class RepeatTemplate implements RepeatOperations {
* @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, RepeatStatus value) {
// Don't re-throw exceptions here: let the exception handler deal with
// that...
@@ -413,9 +413,9 @@ public class RepeatTemplate implements RepeatOperations {
* Delegate to the {@link CompletionPolicy}.
*
* @see org.springframework.batch.repeat.CompletionPolicy#isComplete(RepeatContext,
* ExitStatus)
* RepeatStatus)
*/
protected boolean isComplete(RepeatContext context, ExitStatus result) {
protected boolean isComplete(RepeatContext context, RepeatStatus result) {
boolean complete = completionPolicy.isComplete(context, result);
if (complete) {
logger.debug("Repeat is complete according to policy and result value.");

View File

@@ -1,6 +1,6 @@
package org.springframework.batch.repeat.support;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
/**
@@ -16,7 +16,7 @@ interface ResultHolder {
* @return the result, or null if there is none.
* @throws IllegalStateException
*/
ExitStatus getResult();
RepeatStatus getResult();
/**
* Get the error for client from this holder if any. Does not block if

View File

@@ -16,7 +16,7 @@
package org.springframework.batch.repeat.support;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatException;
@@ -78,7 +78,7 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
* method so there is no need to synchronize access.
*
*/
protected ExitStatus getNextResult(RepeatContext context, RepeatCallback callback, RepeatInternalState state)
protected RepeatStatus getNextResult(RepeatContext context, RepeatCallback callback, RepeatInternalState state)
throws Throwable {
ExecutingRunnable runnable = null;
@@ -161,7 +161,7 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
state.getThrowables().add(future.getError());
}
else {
ExitStatus status = future.getResult();
RepeatStatus status = future.getResult();
result = result && canContinue(status);
executeAfterInterceptors(future.getContext(), status);
}
@@ -192,7 +192,7 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
private final ResultQueue<ResultHolder> queue;
private volatile ExitStatus result;
private volatile RepeatStatus result;
private volatile Throwable error;
@@ -241,7 +241,7 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
* Get the result - never blocks because the queue manages waiting for
* the task to finish.
*/
public ExitStatus getResult() {
public RepeatStatus getResult() {
return result;
}