CHAIN-98 - Refactor command interface and base Implementations to enumeration to represent states. Patch provided by Jonas Sprenger

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/chain/trunk@1499551 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/api/src/main/java/org/apache/commons/chain2/Chain.java b/api/src/main/java/org/apache/commons/chain2/Chain.java
index e57e1b2..83d6d6e 100644
--- a/api/src/main/java/org/apache/commons/chain2/Chain.java
+++ b/api/src/main/java/org/apache/commons/chain2/Chain.java
@@ -22,7 +22,7 @@
  * <p>A {@link Chain} represents a configured list of
  * {@link Command}s that will be executed in order to perform processing
  * on a specified {@link Context}.  Each included {@link Command} will be
- * executed in turn, until either one of them returns <code>true</code>,
+ * executed in turn, until either one of them returns <code>FINISHED</code>,
  * one of the executed {@link Command}s throws an exception,
  * or the end of the chain has been reached.  The {@link Chain} itself will
  * return the return value of the last {@link Command} that was executed
@@ -78,12 +78,12 @@
      * to the following algorithm.</p>
      * <ul>
      * <li>If there are no configured {@link Command}s in the {@link Chain},
-     *     return <code>false</code>.</li>
+     *     return <code>CONTINUE</code>.</li>
      * <li>Call the <code>execute()</code> method of each {@link Command}
      *     configured on this chain, in the order they were added via calls
      *     to the <code>addCommand()</code> method, until the end of the
      *     configured {@link Command}s is encountered, or until one of
-     *     the executed {@link Command}s returns <code>true</code>
+     *     the executed {@link Command}s returns <code>FINISHED</code>
      *     or throws an exception.</li>
      * <li>Walk backwards through the {@link Command}s whose
      *     <code>execute()</code> methods, starting with the last one that
@@ -94,10 +94,10 @@
      *     was called threw an exception, rethrow that exception.</li>
      * <li>Otherwise, return the value returned by the <code>execute()</code>
      *     method of the last {@link Command} that was executed.  This will be
-     *     <code>true</code> if the last {@link Command} indicated that
+     *     <code>FINISHED</code> if the last {@link Command} indicated that
      *     processing of this {@link Context} has been completed, or
-     *     <code>false</code> if none of the called {@link Command}s
-     *     returned <code>true</code>.</li>
+     *     <code>CONTINUE</code> if none of the called {@link Command}s
+     *     returned <code>FINISHED</code>.</li>
      * </ul>
      *
      * @param context The {@link Context} to be processed by this
@@ -106,11 +106,11 @@
      * @exception IllegalArgumentException if <code>context</code>
      *  is <code>null</code>
      *
-     * @return <code>true</code> if the processing of this {@link Context}
-     *  has been completed, or <code>false</code> if the processing
-     *  of this {@link Context} should be delegated to a subsequent
-     *  {@link Command} in an enclosing {@link Chain}
+     * @return {@link Processing#FINISHED} if the processing of this context
+     *  has been completed. Returns {@link Processing#CONTINUE} if the processing
+     *  of this context should be delegated to a subsequent command in an 
+     *  enclosing chain.
      */
-    boolean execute(C context);
+    Processing execute(C context);
 
 }
diff --git a/api/src/main/java/org/apache/commons/chain2/ChainExecutor.java b/api/src/main/java/org/apache/commons/chain2/ChainExecutor.java
index f54ccca..bef9a83 100644
--- a/api/src/main/java/org/apache/commons/chain2/ChainExecutor.java
+++ b/api/src/main/java/org/apache/commons/chain2/ChainExecutor.java
@@ -18,6 +18,8 @@
 
 import java.util.Map;
 
+import org.apache.commons.chain2.Processing;
+
 /**
  * Builder that allows continue adding a command in the target chain and execute it.
  *
@@ -33,10 +35,13 @@
      * Execute the processing represented by the target chain.
      *
      * @param context the context processed by the target chain
-     * @return true, if the processing of the target chain has been completed,
-     *         false otherwise
+     * @return {@link Processing#FINISHED} if the processing of this context
+     *  has been completed. Returns {@link Processing#CONTINUE} if the processing
+     *  of this context should be delegated to a subsequent command in an
+     *  enclosing chain.
+     *  
      * @see Chain#execute(Map)
      */
-    boolean execute(C context);
+    Processing execute(C context);
 
 }
diff --git a/api/src/main/java/org/apache/commons/chain2/Chains.java b/api/src/main/java/org/apache/commons/chain2/Chains.java
index b973d06..1957d33 100644
--- a/api/src/main/java/org/apache/commons/chain2/Chains.java
+++ b/api/src/main/java/org/apache/commons/chain2/Chains.java
@@ -91,7 +91,7 @@
             return this;
         }
 
-        public boolean execute(C context) {
+        public Processing execute(C context) {
             return chain.execute(checkNotNullArgument(context, "Chain cannot be applied to a null context."));
         }
 
diff --git a/api/src/main/java/org/apache/commons/chain2/Command.java b/api/src/main/java/org/apache/commons/chain2/Command.java
index 4ba6f27..fcfad55 100644
--- a/api/src/main/java/org/apache/commons/chain2/Command.java
+++ b/api/src/main/java/org/apache/commons/chain2/Command.java
@@ -18,6 +18,8 @@
 
 import java.util.Map;
 
+import org.apache.commons.chain2.Processing;
+
 /**
  * <p>A {@link Command} encapsulates a unit of processing work to be
  * performed, whose purpose is to examine and/or modify the state of a
@@ -83,29 +85,11 @@
 public interface Command<K, V, C extends Map<K, V>> {
 
     /**
-     * <p>Commands should return <code>CONTINUE_PROCESSING</code> if the processing
-     *  of the given {@link Context} should be delegated to a subsequent
-     *  {@link Command} in an enclosing {@link Chain}.</p>
-     *
-     * @since Chain 1.1
-     */
-    public static final boolean CONTINUE_PROCESSING = false;
-
-    /**
-     * <p>Commands should return <code>PROCESSING_COMPLETE</code>
-     * if the processing of the given {@link Context}
-     *  has been completed.</p>
-     *
-     * @since Chain 1.1
-     */
-    public static final boolean PROCESSING_COMPLETE = true;
-
-    /**
-     * <p>Execute a unit of processing work to be performed.  This
-     * {@link Command} may either complete the required processing
-     * and return <code>true</code>, or delegate remaining processing
-     * to the next {@link Command} in a {@link Chain} containing this
-     * {@link Command} by returning <code>false</code>
+     * Execute a unit of processing work to be performed. 
+     * <p>
+     * A command may either complete the required processing and return 
+     * finished, or delegate remaining processing to the subsequent command 
+     * in the enclosing {@link Chain} by returning continue.
      *
      * @param context The {@link Context} to be processed by this
      *  {@link Command}
@@ -115,11 +99,11 @@
      * @exception IllegalArgumentException if <code>context</code>
      *  is <code>null</code>
      *
-     * @return <code>true</code> if the processing of this {@link Context}
-     *  has been completed, or <code>false</code> if the processing
-     *  of this {@link Context} should be delegated to a subsequent
-     *  {@link Command} in an enclosing {@link Chain}
+     * @return {@link Processing#FINISHED} if the processing of this contex
+     *  has been completed. Returns {@link Processing#CONTINUE} if the processing
+     *  of this context should be delegated to a subsequent command in an 
+     *  enclosing chain.
      */
-    boolean execute(C context);
+    Processing execute(C context);
 
 }
diff --git a/api/src/main/java/org/apache/commons/chain2/Filter.java b/api/src/main/java/org/apache/commons/chain2/Filter.java
index b7497e4..848c47e 100644
--- a/api/src/main/java/org/apache/commons/chain2/Filter.java
+++ b/api/src/main/java/org/apache/commons/chain2/Filter.java
@@ -34,7 +34,7 @@
  * {@link Command}, is where potentially expensive resources must be acquired
  * and held until the processing of a particular request has been completed,
  * even if execution is delegated to a subsequent {@link Command} via the
- * <code>execute()</code> returning <code>false</code>.  A {@link Filter}
+ * <code>execute()</code> returning <code>CONTINUE</code>.  A {@link Filter}
  * can reliably release such resources in the <code>postprocess()</code>
  * method, which is guaranteed to be called by the owning {@link Chain}.</p>
  *
@@ -61,8 +61,8 @@
      *  is <code>null</code>
      *
      * @return If a non-null <code>exception</code> was "handled" by this
-     *  method (and therefore need not be rethrown), return <code>true</code>;
-     *  otherwise return <code>false</code>
+     *  method (and therefore need not be rethrown), return <code>FINISHED</code>;
+     *  otherwise return <code>CONTINUE</code>
      */
    boolean postprocess(C context, Exception exception);
 
diff --git a/api/src/main/java/org/apache/commons/chain2/Processing.java b/api/src/main/java/org/apache/commons/chain2/Processing.java
new file mode 100644
index 0000000..4400c47
--- /dev/null
+++ b/api/src/main/java/org/apache/commons/chain2/Processing.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.chain2;
+
+/**
+ * A <code>Processing</code> encapsulates states that can be returned by
+ * commands. 
+ * <p>
+ * {@link Command}s should either return <code>FINISHED</code> if the
+ * processing of the given context has been completed, or return
+ * <code>CONTINUE</code> if the processing of the given {@link Context} should
+ * be delegated to a subsequent command in an enclosing {@link Chain}.
+ *
+ * @version $Id $
+ */
+public enum Processing {
+
+    /**
+     * Commands should return continue if the processing of the given 
+     * context should be delegated to a subsequent command in an enclosing chain.
+     *
+     * @since Chain 2.0
+     */
+    CONTINUE,
+
+    /**
+     * Commands should return finished if the processing of the given context
+     * has been completed.
+     *
+     * @since Chain 2.0
+     */
+    FINISHED;
+
+}
diff --git a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/agility/impl/HandlerCommand.java b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/agility/impl/HandlerCommand.java
index d2452bb..5e5f17e 100644
--- a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/agility/impl/HandlerCommand.java
+++ b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/agility/impl/HandlerCommand.java
@@ -17,6 +17,7 @@
 package org.apache.commons.chain2.cookbook.agility.impl;
 
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.cookbook.agility.ProcessException;
 import org.apache.commons.chain2.cookbook.agility.Request;
 import org.apache.commons.chain2.cookbook.agility.RequestHandler;
@@ -48,10 +49,10 @@
         }
     }
 
-    public boolean execute(RequestContext requestContext) {
+    public Processing execute(RequestContext requestContext) {
         handle(requestContext);
 
-        return CONTINUE_PROCESSING;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LocaleChange.java b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LocaleChange.java
index d3ef6ab..9310d0f 100644
--- a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LocaleChange.java
+++ b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LocaleChange.java
@@ -17,6 +17,7 @@
 package org.apache.commons.chain2.cookbook.mailreader.commands;
 
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.cookbook.mailreader.MailReader;
 
 import java.io.IOException;
@@ -30,7 +31,7 @@
     public LocaleChange() {
     }
 
-    public boolean execute(MailReader mailReader) {
+    public Processing execute(MailReader mailReader) {
         try {
             Writer logger = mailReader.getLogger();
             logger.write("LocaleChange.execute ");
@@ -39,7 +40,7 @@
             throw new RuntimeException(ioe);
         }
 
-        return CONTINUE_PROCESSING;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LogonUser.java b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LogonUser.java
index 9837228..a017e73 100644
--- a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LogonUser.java
+++ b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/LogonUser.java
@@ -17,6 +17,7 @@
 package org.apache.commons.chain2.cookbook.mailreader.commands;
 
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.cookbook.mailreader.MailReader;
 
 import java.io.IOException;
@@ -29,14 +30,14 @@
     public LogonUser() {
     }
 
-    public boolean execute(MailReader mailReader) {
+    public Processing execute(MailReader mailReader) {
         try {
             mailReader.getLogger().write("LogonUser.execute");
         } catch (IOException ioe) {
             throw new RuntimeException(ioe);
         }
 
-        return CONTINUE_PROCESSING;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/ProfileCheck.java b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/ProfileCheck.java
index 2192062..3156388 100644
--- a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/ProfileCheck.java
+++ b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/commands/ProfileCheck.java
@@ -17,6 +17,7 @@
 package org.apache.commons.chain2.cookbook.mailreader.commands;
 
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.cookbook.mailreader.MailReader;
 
 import java.io.IOException;
@@ -29,14 +30,14 @@
     public ProfileCheck() {
     }
 
-    public boolean execute(MailReader mailReader) {
+    public Processing execute(MailReader mailReader) {
         try {
             mailReader.getLogger().write("ProfileCheck.execute\n");
         } catch (IOException ioe) {
             throw new RuntimeException(ioe);
         }
 
-        return CONTINUE_PROCESSING;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/profile/ProfileCheck.java b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/profile/ProfileCheck.java
index 0f1aa33..c5b70b2 100644
--- a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/profile/ProfileCheck.java
+++ b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/profile/ProfileCheck.java
@@ -17,6 +17,7 @@
 package org.apache.commons.chain2.cookbook.profile;
 
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Processing;
 
 /**
  * @version $Id$
@@ -25,13 +26,13 @@
 
     public Profile newProfile(ProfileContext context) { return new Profile(); }
 
-    public boolean execute(ProfileContext context) {
+    public Processing execute(ProfileContext context) {
         Profile profile = context.getProfile();
 
         if (null == profile) {
             context.setProfile(newProfile(context));
         }
-        return false;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/apps/cookbook-examples/src/test/java/org/apache/commons/chain2/cookbook/mailreader/CatalogTest.java b/apps/cookbook-examples/src/test/java/org/apache/commons/chain2/cookbook/mailreader/CatalogTest.java
index b5c10ce..ef29816 100644
--- a/apps/cookbook-examples/src/test/java/org/apache/commons/chain2/cookbook/mailreader/CatalogTest.java
+++ b/apps/cookbook-examples/src/test/java/org/apache/commons/chain2/cookbook/mailreader/CatalogTest.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Processing;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -28,6 +29,7 @@
 import javax.servlet.http.HttpSession;
 import java.util.Locale;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.verify;
@@ -51,8 +53,8 @@
         when(request.getSession()).thenReturn(session);
     }
 
-    private boolean executeCatalogCommand(MailReader context,
-            String name, HttpServletRequest request) {
+    private Processing executeCatalogCommand(MailReader context,
+                                             String name, HttpServletRequest request) {
 
         ServletContext servletContext =
                 request.getSession().getServletContext();
@@ -73,9 +75,9 @@
         MailReader context = new MailReader();
         context.setLocale(Locale.CANADA);
 
-        when(testCommand.execute(context)).thenReturn(true);
+        when(testCommand.execute(context)).thenReturn(Processing.FINISHED);
 
-        assertTrue("Catalog execution did not complete as expected",
+        assertEquals("Catalog execution did not complete as expected", Processing.FINISHED,
                 executeCatalogCommand(context, "aCommand", request));
 
         verify(testCommand).execute(context);
diff --git a/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java b/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java
index 469c05e..35908e2 100644
--- a/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java
+++ b/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.web.WebContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -58,15 +59,15 @@
      * <p>Execute the command.</p>
      *
      * @param context The {@link Context} we are operating on
-     * @return <code>false</code> so that processng will continue
+     * @return {@link Processing#CONTINUE} so that processing will continue.
      */
-    public boolean execute(WebContext<String, Object> context) {
+    public Processing execute(WebContext<String, Object> context) {
         count++;
         log.info("Executing: " + attribute + "=" + count);
 
         context.getSessionScope().put(attribute, new Integer(count));
 
-        return false;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java b/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java
index df5c414..0935118 100644
--- a/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java
+++ b/apps/example1/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.web.servlet.ServletWebContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -59,9 +60,9 @@
      * <p>Execute the command.</p>
      *
      * @param context The {@link Context} we are operating on
-     * @return <code>false</code> so that processng will continue
+     * @return {@link Processing#CONTINUE} so that processing will continue.
      */
-    public boolean execute(ServletWebContext<String, Object> context) {
+    public Processing execute(ServletWebContext<String, Object> context) {
         try {
             String uri = getForward(context);
             if (uri != null) {
@@ -70,12 +71,12 @@
                 }
                 RequestDispatcher rd = context.getContext().getRequestDispatcher(uri);
                 rd.forward(context.getRequest(), context.getResponse());
-                return true;
+                return Processing.FINISHED;
             } else {
                 if (log.isDebugEnabled()) {
                     log.debug("No forward found");
                 }
-                return false;
+                return Processing.CONTINUE;
             }
         } catch (ServletException e) {
             throw new RuntimeException(e);
diff --git a/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java b/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java
index 469c05e..8fcf582 100644
--- a/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java
+++ b/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/CountCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.web.WebContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -57,16 +58,17 @@
     /**
      * <p>Execute the command.</p>
      *
-     * @param context The {@link Context} we are operating on
-     * @return <code>false</code> so that processng will continue
+     *
+     * @param context The {@link org.apache.commons.chain2.Context} we are operating on
+     * @return {@link Processing#CONTINUE} so that processing will continue.
      */
-    public boolean execute(WebContext<String, Object> context) {
+    public Processing execute(WebContext<String, Object> context) {
         count++;
         log.info("Executing: " + attribute + "=" + count);
 
         context.getSessionScope().put(attribute, new Integer(count));
 
-        return false;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java b/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java
index 358d5c1..6931c63 100644
--- a/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java
+++ b/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ForwardCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.web.servlet.ServletWebContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -57,10 +58,11 @@
     /**
      * <p>Execute the command.</p>
      *
-     * @param context The {@link Context} we are operating on
-     * @return <code>false</code> so that processng will continue
+     *
+     * @param context The {@link org.apache.commons.chain2.Context} we are operating on
+     * @return {@link Processing#CONTINUE} so that processnig will continue.
      */
-    public boolean execute(ServletWebContext<String, Object> context) {
+    public Processing execute(ServletWebContext<String, Object> context) {
         try {
             String uri = getForward(context);
             if (uri != null) {
@@ -69,12 +71,12 @@
                 }
                 RequestDispatcher rd = context.getContext().getRequestDispatcher(uri);
                 rd.forward(context.getRequest(), context.getResponse());
-                return true;
+                return Processing.FINISHED;
             } else {
                 if (log.isDebugEnabled()) {
                     log.debug("No forward found");
                 }
-                return false;
+                return Processing.CONTINUE;
             }
         } catch (IOException e) {
             throw new RuntimeException(e);
diff --git a/base/src/main/java/org/apache/commons/chain2/base/CopyCommand.java b/base/src/main/java/org/apache/commons/chain2/base/CopyCommand.java
index 054a9bc..8910bc7 100644
--- a/base/src/main/java/org/apache/commons/chain2/base/CopyCommand.java
+++ b/base/src/main/java/org/apache/commons/chain2/base/CopyCommand.java
@@ -19,6 +19,7 @@
 import java.util.Map;
 
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Processing;
 
 /**
  * <p>Copy a specified literal value, or a context attribute stored under
@@ -78,17 +79,17 @@
      * <p>Copy a specified literal value, or a context attribute stored under
      * the <code>fromKey</code> (if any), to the <code>toKey</code>.</p>
      *
-     * @param context {@link org.apache.commons.chain2.Context Context} in which we are operating
+     * @param context {@link org.apache.commons.chain2.Context Context} in which we are operating.
      *
-     * @return <code>false</code> so that processing will continue
+     * @return {@link Processing#CONTINUE} so that processing will continue.
      * @throws org.apache.commons.chain2.ChainException in the if an error occurs during execution.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         if (containsKeys(context)) {
             V value = context.get(getFromKey());
             context.put(getToKey(), value);
         }
-        return false;
+        return Processing.CONTINUE;
     }
 
     private boolean containsKeys(C context) {
diff --git a/base/src/main/java/org/apache/commons/chain2/base/DispatchCommand.java b/base/src/main/java/org/apache/commons/chain2/base/DispatchCommand.java
index cf398a5..d7e3aa1 100644
--- a/base/src/main/java/org/apache/commons/chain2/base/DispatchCommand.java
+++ b/base/src/main/java/org/apache/commons/chain2/base/DispatchCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -55,7 +56,7 @@
 
     /**
      * Look up the method specified by either "method" or "methodKey" and invoke it,
-     * returning a boolean value as interpreted by <code>evaluateResult</code>.
+     * returning a {@link Processing} value as interpreted by <code>evaluateResult</code>.
      * @param context The Context to be processed by this Command.
      * @return the result of method being dispatched to.
      * @throws IllegalStateException if neither 'method' nor 'methodKey' properties are defined
@@ -64,7 +65,7 @@
      * the exception itself, unless the cause is an <code>Error</code> or other <code>Throwable</code>
      * which is not an <code>Exception</code>.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         if (this.getMethod() == null && this.getMethodKey() == null) {
             throw new IllegalStateException("Neither 'method' nor 'methodKey' properties are defined ");
         }
@@ -120,15 +121,19 @@
     }
 
     /**
-     * Evaluate the result of the method invocation as a boolean value.  Base implementation
-     * expects that the invoked method returns boolean true/false, but subclasses might
+     * Evaluate the result of the method invocation as a processing value. Base implementation
+     * expects that the invoked method returns processing, but subclasses might
      * implement other interpretations.
-     * @param o The result of the methid execution
+     * @param obj The result of the method execution
      * @return The evaluated result/
      */
-    protected boolean evaluateResult(Object o) {
-        Boolean result = (Boolean) o;
-        return result != null && result.booleanValue();
+    protected Processing evaluateResult(Object obj) {
+        if(obj instanceof Processing) {
+            Processing result = (Processing) obj;
+            return result;
+        } else {
+            return Processing.CONTINUE;
+        }
     }
 
     /**
diff --git a/base/src/main/java/org/apache/commons/chain2/base/DispatchLookupCommand.java b/base/src/main/java/org/apache/commons/chain2/base/DispatchLookupCommand.java
index 67c6b01..a753a26 100644
--- a/base/src/main/java/org/apache/commons/chain2/base/DispatchLookupCommand.java
+++ b/base/src/main/java/org/apache/commons/chain2/base/DispatchLookupCommand.java
@@ -20,6 +20,7 @@
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
 import org.apache.commons.chain2.Filter;
+import org.apache.commons.chain2.Processing;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -133,13 +134,13 @@
      *
      * @param context The context for this request
      * @return the result of executing the looked-up command's method, or
-     * <code>false</code> if no command is found.
+     * {@link Processing#CONTINUE} if no command is found.
      *
      * @throws DispatchException if no such {@link Command} can be found and the
      *  <code>optional</code> property is set to <code>false</code>
      */
     @Override
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         if (this.getMethod() == null && this.getMethodKey() == null) {
             throw new IllegalStateException("Neither 'method' nor 'methodKey' properties are defined");
         }
@@ -150,9 +151,13 @@
             try {
                 Method methodObject = extractMethod(command, context);
                 Object obj = methodObject.invoke(command, getArguments(context));
-
-                Boolean result = (Boolean) obj;
-                return result != null && result.booleanValue(); // might cause NPE (obj could be null)
+                
+                if(obj instanceof Processing) {
+                    Processing result = (Processing) obj;
+                    return result;
+                } else {
+                    return Processing.CONTINUE;
+                }
             } catch (NoSuchMethodException e) {
                 throw new DispatchException("Error extracting method from context", e, context, this);
             } catch (IllegalAccessException e) {
@@ -162,7 +167,7 @@
                 throw new DispatchException("Error in reflected dispatched command", cause, context, this);
             }
         }
-        return false;
+        return Processing.CONTINUE;
     }
 
     // ------------------------------------------------------ Protected Methods
diff --git a/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java b/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java
index 147bf0d..717a75e 100644
--- a/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java
+++ b/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java
@@ -21,6 +21,7 @@
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
 import org.apache.commons.chain2.Filter;
+import org.apache.commons.chain2.Processing;
 
 import java.util.Map;
 
@@ -254,7 +255,7 @@
      * <p>Look up the specified command, and (if found) execute it.
      * Unless <code>ignoreExecuteResult</code> is set to <code>true</code>,
      * return the result of executing the found command.  If no command
-     * is found, return <code>false</code>, unless the <code>optional</code>
+     * is found, return {@link Processing#CONTINUE}, unless the <code>optional</code>
      * property is <code>false</code>, in which case an <code>IllegalArgumentException</code>
      * will be thrown.
      * </p>
@@ -265,21 +266,21 @@
      *  can be found and the <code>optional</code> property is set
      *  to <code>false</code>
      * @return the result of executing the looked-up command, or
-     * <code>false</code> if no command is found or if the command
+     * <code>CONTINUE</code> if no command is found or if the command
      * is found but the <code>ignoreExecuteResult</code> property of this
      * instance is <code>true</code>
      * @throws org.apache.commons.chain2.ChainException if and error occurs in the looked-up Command.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         Command<K, V, C> command = getCommand(context);
         if (command != null) {
-            boolean result = command.execute(context);
+            Processing result = command.execute(context);
             if (isIgnoreExecuteResult()) {
-                return false;
+                return Processing.CONTINUE;
             }
             return result;
         }
-        return false;
+        return Processing.CONTINUE;
     }
 
 
diff --git a/base/src/main/java/org/apache/commons/chain2/base/OverrideCommand.java b/base/src/main/java/org/apache/commons/chain2/base/OverrideCommand.java
index 6695857..dfa0b09 100644
--- a/base/src/main/java/org/apache/commons/chain2/base/OverrideCommand.java
+++ b/base/src/main/java/org/apache/commons/chain2/base/OverrideCommand.java
@@ -21,6 +21,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 /**
  * <p>Override any context attribute stored under the <code>key</code> with <code>value</code>.</p>
@@ -79,14 +80,14 @@
      *
      * @param context {@link org.apache.commons.chain2.Context} in which we are operating
      *
-     * @return <code>false</code> so that processing will continue
+     * @return {@link Processing#CONTINUE} so that {@link Processing} will continue.
      * @throws org.apache.commons.chain2.ChainException if and error occurs.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         if (context.containsKey(getKey())) {
             context.put(getKey(), getValue());
         }
-        return false;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/base/src/main/java/org/apache/commons/chain2/base/RemoveCommand.java b/base/src/main/java/org/apache/commons/chain2/base/RemoveCommand.java
index 2d8e977..a647869 100644
--- a/base/src/main/java/org/apache/commons/chain2/base/RemoveCommand.java
+++ b/base/src/main/java/org/apache/commons/chain2/base/RemoveCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 /**
  * <p>Remove any context attribute stored under the <code>fromKey</code>.</p>
@@ -59,12 +60,12 @@
      *
      * @param context {@link Context} in which we are operating
      *
-     * @return <code>false</code> so that processing will continue
+     * @return {@link Processing#CONTINUE} so that processing will continue.
      * @throws org.apache.commons.chain2.ChainException if and error occurs.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         context.remove(getFromKey());
-        return false;
+        return Processing.CONTINUE;
     }
 
 }
diff --git a/base/src/main/java/org/apache/commons/chain2/impl/ChainBase.java b/base/src/main/java/org/apache/commons/chain2/impl/ChainBase.java
index 023860e..4b8b8e4 100644
--- a/base/src/main/java/org/apache/commons/chain2/impl/ChainBase.java
+++ b/base/src/main/java/org/apache/commons/chain2/impl/ChainBase.java
@@ -21,6 +21,7 @@
 import org.apache.commons.chain2.Context;
 import org.apache.commons.chain2.Filter;
 import org.apache.commons.chain2.ChainException;
+import org.apache.commons.chain2.Processing;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -145,12 +146,12 @@
      * @throws IllegalArgumentException if <code>context</code>
      *  is <code>null</code>
      *
-     * @return <code>true</code> if the processing of this {@link Context}
-     *  has been completed, or <code>false</code> if the processing
-     *  of this {@link Context} should be delegated to a subsequent
-     *  {@link Command} in an enclosing {@link Chain}
+     * @return {@link Processing#FINISHED} if the processing of this contex
+     *  has been completed. Returns {@link Processing#CONTINUE} if the processing
+     *  of this context should be delegated to a subsequent command in an 
+     *  enclosing chain.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         // Verify our parameters
         if (context == null) {
             throw new IllegalArgumentException("Can't execute a null context");
@@ -159,9 +160,9 @@
         // Freeze the configuration of the command list
         frozen = true;
 
-        // Execute the commands in this list until one returns true
-        // or throws an exception
-        boolean saveResult = false;
+        // Execute the commands in this list until one returns something else 
+        // than Processing.CONTINUE or throws an exception
+        Processing saveResult = Processing.CONTINUE;
         Exception saveException = null;
         int i = 0;
         int n = commands.size();
@@ -170,7 +171,11 @@
             try {
                 lastCommand = commands.get(i);
                 saveResult = lastCommand.execute(context);
-                if (saveResult) {
+                if(saveResult == null) {
+                    String format = String.format("The command '%s' returned an invalid processing value: '%s'",
+                            lastCommand.getClass().getName(), saveResult);
+                    throw new ChainException(format);
+                } else if (saveResult != Processing.CONTINUE) {
                     break;
                 }
             } catch (Exception e) {
diff --git a/base/src/test/java/org/apache/commons/chain2/base/CopyCommandTestCase.java b/base/src/test/java/org/apache/commons/chain2/base/CopyCommandTestCase.java
index 86e3cd2..56533df 100644
--- a/base/src/test/java/org/apache/commons/chain2/base/CopyCommandTestCase.java
+++ b/base/src/test/java/org/apache/commons/chain2/base/CopyCommandTestCase.java
@@ -27,6 +27,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.commons.chain2.Processing;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -94,8 +95,8 @@
     }
 
     private void execute() {
-        // make sure execute always returns false
-        assertFalse(command.execute(context));
+        // make sure execute always returns continue
+        assertEquals(Processing.CONTINUE, command.execute(context));
     }
 
 }
diff --git a/base/src/test/java/org/apache/commons/chain2/base/DispatchCommandTestCase.java b/base/src/test/java/org/apache/commons/chain2/base/DispatchCommandTestCase.java
index 44fc5e1..d3c2bd3 100644
--- a/base/src/test/java/org/apache/commons/chain2/base/DispatchCommandTestCase.java
+++ b/base/src/test/java/org/apache/commons/chain2/base/DispatchCommandTestCase.java
@@ -17,6 +17,7 @@
 package org.apache.commons.chain2.base;
 
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.impl.ContextBase;
 import org.junit.Test;
 
@@ -32,8 +33,8 @@
         test.setMethod("testMethod");
         Context<String, Object> context = new ContextBase();
         assertNull(context.get("foo"));
-        boolean result = test.execute(context);
-        assertTrue(result);
+        Processing result = test.execute(context);
+        assertEquals(Processing.FINISHED, result);
         assertNotNull(context.get("foo"));
         assertEquals("foo", context.get("foo"));
 
@@ -49,8 +50,8 @@
         Context<String, Object> context = new ContextBase();
         context.put("foo", "testMethodKey");
         assertNull(context.get("bar"));
-        boolean result = test.execute(context);
-        assertFalse(result);
+        Processing result = test.execute(context);
+        assertEquals(Processing.CONTINUE, result);
         assertNotNull(context.get("bar"));
         assertEquals("bar", context.get("bar"));
 
@@ -64,8 +65,8 @@
         test.setMethod("foo");
         Context<String, Object> context = new ContextBase();
         assertNull(context.get("elephant"));
-        boolean result = test.execute(context);
-        assertTrue(result);
+        Processing result = test.execute(context);
+        assertEquals(Processing.FINISHED, result);
         assertNotNull(context.get("elephant"));
         assertEquals("elephant", context.get("elephant"));
 
@@ -76,15 +77,15 @@
     class TestCommand extends DispatchCommand<String, Object, Context<String, Object>> {
 
 
-        public boolean testMethod(Context<String, Object> context) {
+        public Processing testMethod(Context<String, Object> context) {
             context.put("foo", "foo");
-            return true;
+            return Processing.FINISHED;
         }
 
-        public boolean testMethodKey(Context<String, Object> context) {
+        public Processing testMethodKey(Context<String, Object> context) {
 
             context.put("bar", "bar");
-            return false;
+            return Processing.CONTINUE;
         }
 
     }
@@ -106,9 +107,9 @@
             return new Object[] { new TestAlternateContext(context) };
         }
 
-        public boolean foo(TestAlternateContext context) {
+        public Processing foo(TestAlternateContext context) {
             context.put("elephant", "elephant");
-            return true;
+            return Processing.FINISHED;
         }
 
     }
diff --git a/base/src/test/java/org/apache/commons/chain2/base/DispatchLookupCommandTestCase.java b/base/src/test/java/org/apache/commons/chain2/base/DispatchLookupCommandTestCase.java
index 1691f70..7532b19 100644
--- a/base/src/test/java/org/apache/commons/chain2/base/DispatchLookupCommandTestCase.java
+++ b/base/src/test/java/org/apache/commons/chain2/base/DispatchLookupCommandTestCase.java
@@ -17,6 +17,7 @@
 package org.apache.commons.chain2.base;
 
 import static org.apache.commons.chain2.testutils.HasLog.hasLog;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -24,6 +25,7 @@
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.impl.CatalogBase;
 import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.chain2.impl.ContextBase;
@@ -102,8 +104,8 @@
         command.setMethod("fooMethod");
 
         try {
-            assertTrue("Command should return true",
-                       command.execute(context));
+            assertEquals("Command should return finished",
+                    Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
 
             fail("Threw exception: " + e);
@@ -113,8 +115,8 @@
         command.setMethod("barMethod");
 
         try {
-            assertTrue("Command should return true",
-                       command.execute(context));
+            assertEquals("Command should return finished",
+                    Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -160,8 +162,8 @@
         context.put("methodKey", "fooMethod");
 
         try {
-            assertTrue("Command should return true",
-                       command.execute(context));
+            assertEquals("Command should return finished",
+                    Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -172,8 +174,8 @@
 
 
         try {
-            assertTrue("Command should return true",
-                       command.execute(context));
+            assertEquals("Command should return finished",
+                    Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -193,14 +195,14 @@
             super(id);
         }
 
-        public boolean fooMethod(C context) {
+        public Processing fooMethod(C context) {
             log(context, id);
-            return true;
+            return Processing.FINISHED;
         }
 
-        public boolean barMethod(C context) {
+        public Processing barMethod(C context) {
             log(context, id);
-            return true;
+            return Processing.FINISHED;
         }
 
     }
diff --git a/base/src/test/java/org/apache/commons/chain2/base/LookupCommandTestCase.java b/base/src/test/java/org/apache/commons/chain2/base/LookupCommandTestCase.java
index ecf23c4..bcf8970 100644
--- a/base/src/test/java/org/apache/commons/chain2/base/LookupCommandTestCase.java
+++ b/base/src/test/java/org/apache/commons/chain2/base/LookupCommandTestCase.java
@@ -28,6 +28,7 @@
 import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.Chain;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.impl.CatalogBase;
 import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.chain2.impl.ChainBase;
@@ -104,8 +105,8 @@
         command.setName("foo");
 
         try {
-            assertTrue("Command should return true",
-                       command.execute(context));
+            assertEquals("Command should return finished",
+        	    Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -127,8 +128,8 @@
         command.setName("foo");
 
         try {
-            assertTrue("Command should return true",
-                    command.execute(context));
+            assertEquals("Command should return finished",
+        	    Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -146,8 +147,8 @@
         context.put("nameKey", "foo");
 
         try {
-            assertTrue("Command should return true",
-                    command.execute(context));
+            assertEquals("Command should return finished",
+                Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -170,8 +171,8 @@
         context.put("nameKey", "foo");
 
         try {
-            assertTrue("Command should return true",
-                       command.execute(context));
+            assertEquals("Command should return finished",
+                Processing.FINISHED, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -189,8 +190,8 @@
         command.setName("foo");
 
         try {
-            assertFalse("Command should return false",
-                       command.execute(context));
+            assertEquals("Command should return continue",
+                Processing.CONTINUE, command.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
diff --git a/base/src/test/java/org/apache/commons/chain2/impl/ChainBaseTestCase.java b/base/src/test/java/org/apache/commons/chain2/impl/ChainBaseTestCase.java
index 45ff9ad..5341bb1 100644
--- a/base/src/test/java/org/apache/commons/chain2/impl/ChainBaseTestCase.java
+++ b/base/src/test/java/org/apache/commons/chain2/impl/ChainBaseTestCase.java
@@ -28,8 +28,10 @@
 import java.util.List;
 
 import org.apache.commons.chain2.Chain;
+import org.apache.commons.chain2.ChainException;
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.testutils.AddingCommand;
 import org.apache.commons.chain2.testutils.DelegatingCommand;
 import org.apache.commons.chain2.testutils.DelegatingFilter;
@@ -37,6 +39,7 @@
 import org.apache.commons.chain2.testutils.ExceptionFilter;
 import org.apache.commons.chain2.testutils.NonDelegatingCommand;
 import org.apache.commons.chain2.testutils.NonDelegatingFilter;
+import org.apache.commons.chain2.testutils.NullReturningCommand;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -91,6 +94,15 @@
     // ------------------------------------------------ Individual Test Methods
 
 
+    @Test (expected = ChainException.class)
+    public void nullReturningCommandForcesException() {
+        chain.addCommand(new DelegatingCommand("BeforeNullReturningCommand"));
+        chain.addCommand(new NullReturningCommand());
+        chain.addCommand(new NonDelegatingCommand("AfterNullReturningCommand"));
+        
+        chain.execute(context);
+    }
+    
     // Test the ability to add commands
     @Test
     public void testCommands() {
@@ -117,8 +129,7 @@
     public void testExecute1a() {
         chain.addCommand(new NonDelegatingCommand("1"));
         try {
-            assertTrue("Chain returned true",
-                       chain.execute(context));
+            assertEquals(Processing.FINISHED, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -131,8 +142,7 @@
     public void testExecute1b() {
         chain.addCommand(new DelegatingCommand("1"));
         try {
-            assertTrue("Chain returned false",
-                       !chain.execute(context));
+            assertEquals(Processing.CONTINUE, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -177,8 +187,7 @@
         chain.addCommand(new DelegatingCommand("2"));
         chain.addCommand(new NonDelegatingCommand("3"));
         try {
-            assertTrue("Chain returned true",
-                       chain.execute(context));
+            assertEquals(Processing.FINISHED, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -193,8 +202,7 @@
         chain.addCommand(new DelegatingCommand("2"));
         chain.addCommand(new DelegatingCommand("3"));
         try {
-            assertTrue("Chain returned false",
-                       !chain.execute(context));
+            assertEquals(Processing.CONTINUE, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -241,8 +249,7 @@
     public void testExecute3a() {
         chain.addCommand(new NonDelegatingFilter("1", "a"));
         try {
-            assertTrue("Chain returned true",
-                       chain.execute(context));
+            assertEquals(Processing.FINISHED, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -255,8 +262,7 @@
     public void testExecute3b() {
         chain.addCommand(new DelegatingFilter("1", "a"));
         try {
-            assertTrue("Chain returned false",
-                       !chain.execute(context));
+            assertEquals(Processing.CONTINUE, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -286,8 +292,7 @@
         chain.addCommand(new DelegatingCommand("2"));
         chain.addCommand(new NonDelegatingFilter("3", "c"));
         try {
-            assertTrue("Chain returned true",
-                       chain.execute(context));
+            assertEquals(Processing.FINISHED, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
@@ -302,8 +307,7 @@
         chain.addCommand(new DelegatingFilter("2", "b"));
         chain.addCommand(new DelegatingCommand("3"));
         try {
-            assertTrue("Chain returned false",
-                       !chain.execute(context));
+            assertEquals(Processing.CONTINUE, chain.execute(context));
         } catch (Exception e) {
             fail("Threw exception: " + e);
         }
diff --git a/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/TestCommand.java b/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/TestCommand.java
index 73f4d23..504cb5b 100644
--- a/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/TestCommand.java
+++ b/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/TestCommand.java
@@ -19,6 +19,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 
 /**
@@ -47,8 +48,8 @@
     }
 
 
-    public boolean execute(Context<String, Object> context) {
-    return (false);
+    public Processing execute(Context<String, Object> context) {
+        return Processing.CONTINUE;
     }
 
 
diff --git a/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java b/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java
index b002cfa..8cf9e3f 100644
--- a/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java
+++ b/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java
@@ -33,6 +33,7 @@
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.testutils.AddingCommand;
 import org.apache.commons.chain2.impl.CatalogBase;
 import org.apache.commons.chain2.impl.ChainBase;
@@ -160,8 +161,7 @@
     @Test
     public void testExecute2a() throws Exception {
 
-        assertTrue("Chain returned true",
-                catalog.getCommand("Execute2a").execute(context));
+        assertEquals(Processing.FINISHED, catalog.getCommand("Execute2a").execute(context));
         assertThat(context, hasLog("1/2/3"));
 
     }
@@ -171,8 +171,7 @@
     @Test
     public void testExecute2b() throws Exception {
 
-        assertFalse("Chain returned false",
-                catalog.getCommand("Execute2b").execute(context));
+        assertEquals(Processing.CONTINUE, catalog.getCommand("Execute2b").execute(context));
         assertThat(context, hasLog("1/2/3"));
 
     }
@@ -212,8 +211,7 @@
     @Test
     public void testExecute4a() throws Exception {
 
-        assertTrue("Chain returned true",
-                catalog.getCommand("Execute4a").execute(context));
+        assertEquals(Processing.FINISHED, catalog.getCommand("Execute4a").execute(context));
         assertThat(context, hasLog("1/2/3/c/a"));
 
     }
@@ -223,8 +221,7 @@
     @Test
     public void testExecute4b() throws Exception {
 
-        assertFalse("Chain returned false",
-                catalog.getCommand("Execute4b").execute(context));
+        assertEquals(Processing.CONTINUE, catalog.getCommand("Execute4b").execute(context));
         assertThat(context, hasLog("1/2/3/b"));
 
     }
diff --git a/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingCommand.java b/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingCommand.java
index e099d58..f57a99b 100644
--- a/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingCommand.java
+++ b/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingCommand.java
@@ -19,6 +19,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 
 /**
@@ -50,10 +51,10 @@
 
     // Execution method for this Command
     @Override
-    public boolean execute(Context<String, Object> context) {
+    public Processing execute(Context<String, Object> context) {
 
         super.execute(context);
-        return (false);
+        return Processing.CONTINUE;
 
     }
 
diff --git a/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingFilter.java b/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingFilter.java
index 2789f90..5f97ada 100644
--- a/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingFilter.java
+++ b/test-utils/src/main/java/org/apache/commons/chain2/testutils/DelegatingFilter.java
@@ -19,6 +19,7 @@
 
 import org.apache.commons.chain2.Context;
 import org.apache.commons.chain2.Filter;
+import org.apache.commons.chain2.Processing;
 
 
 /**
@@ -50,10 +51,10 @@
 
     // Execution method for this Command
     @Override
-    public boolean execute(Context<String, Object> context) {
+    public Processing execute(Context<String, Object> context) {
 
         super.execute(context);
-        return (false);
+        return Processing.CONTINUE;
 
     }
 
diff --git a/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingCommand.java b/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingCommand.java
index 3b5e471..2627b55 100644
--- a/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingCommand.java
+++ b/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingCommand.java
@@ -19,6 +19,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 
 /**
@@ -64,13 +65,13 @@
 
 
     // Execution method for this Command
-    public boolean execute(Context<String, Object> context) {
+    public Processing execute(Context<String, Object> context) {
 
         if (context == null) {
             throw new IllegalArgumentException();
         }
         log(context, id);
-        return (true);
+        return Processing.FINISHED;
 
     }
 
diff --git a/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingFilter.java b/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingFilter.java
index b5d29f0..78d8a8c 100644
--- a/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingFilter.java
+++ b/test-utils/src/main/java/org/apache/commons/chain2/testutils/NonDelegatingFilter.java
@@ -19,6 +19,7 @@
 
 import org.apache.commons.chain2.Context;
 import org.apache.commons.chain2.Filter;
+import org.apache.commons.chain2.Processing;
 
 
 /**
@@ -64,10 +65,10 @@
 
     // Execution method for this Command
     @Override
-    public boolean execute(Context<String, Object> context) {
+    public Processing execute(Context<String, Object> context) {
 
         super.execute(context);
-        return (true);
+        return Processing.FINISHED;
 
     }
 
diff --git a/test-utils/src/main/java/org/apache/commons/chain2/testutils/NullReturningCommand.java b/test-utils/src/main/java/org/apache/commons/chain2/testutils/NullReturningCommand.java
new file mode 100644
index 0000000..f9d83e0
--- /dev/null
+++ b/test-utils/src/main/java/org/apache/commons/chain2/testutils/NullReturningCommand.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.commons.chain2.testutils;
+
+
+import org.apache.commons.chain2.Chain;
+import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
+
+
+/**
+ * Implementation of {@link Command} that always returns null if it's executed.
+ * 
+ * @version $Id $
+ */
+public class NullReturningCommand extends DelegatingCommand {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    public NullReturningCommand() {
+        this("");
+    }
+
+
+    // Construct an instance that will log the specified identifier
+    public NullReturningCommand(String id) {
+        super(id);
+    }
+
+
+    // -------------------------------------------------------- Command Methods
+    
+    @Override
+    public Processing execute(Context<String, Object> context) {
+        return null;
+    }
+    
+    
+    public Processing execute(Context<String, Object> context, Chain<String, Object, Context<String, Object>> chain) {
+        return null;
+    }
+
+
+}
diff --git a/test-utils/src/test/java/org/apache/commons/chain2/testutils/NonDelegatingCommandTestCase.java b/test-utils/src/test/java/org/apache/commons/chain2/testutils/NonDelegatingCommandTestCase.java
index 9b59388..2e73ab0 100644
--- a/test-utils/src/test/java/org/apache/commons/chain2/testutils/NonDelegatingCommandTestCase.java
+++ b/test-utils/src/test/java/org/apache/commons/chain2/testutils/NonDelegatingCommandTestCase.java
@@ -18,14 +18,15 @@
 package org.apache.commons.chain2.testutils;
 
 import static org.apache.commons.chain2.testutils.HasLog.hasLog;
-import static org.hamcrest.Matchers.is;
 import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
 import static org.hamcrest.collection.IsMapContaining.hasKey;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
 
 import java.util.UUID;
 
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -68,6 +69,6 @@
     }
 
     private void execute() {
-        assertThat(command.execute(context), is(true));
+        assertEquals(Processing.FINISHED, command.execute(context));
     }
 }
diff --git a/web/src/main/java/org/apache/commons/chain2/web/AbstractGetLocaleCommand.java b/web/src/main/java/org/apache/commons/chain2/web/AbstractGetLocaleCommand.java
index 8fc238b..51399ac 100644
--- a/web/src/main/java/org/apache/commons/chain2/web/AbstractGetLocaleCommand.java
+++ b/web/src/main/java/org/apache/commons/chain2/web/AbstractGetLocaleCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 import java.util.Locale;
 
@@ -68,12 +69,12 @@
      *
      * @param context The {@link Context} we are operating on
      *
-     * @return <code>false</code> so that processng will continue
+     * @return {@link Processing#CONTINUE} so that the processing will continue.
      * @throws org.apache.commons.chain2.ChainException If an error occurs during execution.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         context.put(getLocaleKey(), getLocale(context));
-        return (false);
+        return Processing.CONTINUE;
     }
 
     // ------------------------------------------------------- Protected Methods
diff --git a/web/src/main/java/org/apache/commons/chain2/web/AbstractSetLocaleCommand.java b/web/src/main/java/org/apache/commons/chain2/web/AbstractSetLocaleCommand.java
index 5dff19f..28c52b4 100644
--- a/web/src/main/java/org/apache/commons/chain2/web/AbstractSetLocaleCommand.java
+++ b/web/src/main/java/org/apache/commons/chain2/web/AbstractSetLocaleCommand.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Command;
 import org.apache.commons.chain2.Context;
+import org.apache.commons.chain2.Processing;
 
 import java.util.Locale;
 
@@ -69,12 +70,12 @@
      *
      * @param context The {@link Context} we are operating on
      *
-     * @return <code>false</code> so that processing will continue
+     * @return {@link Processing#CONTINUE} so that the processing will continue.
      * @throws org.apache.commons.chain2.ChainException If an error occurs during execution.
      */
-    public boolean execute(C context) {
+    public Processing execute(C context) {
         setLocale(context, (Locale) context.get(getLocaleKey()));
-        return (false);
+        return Processing.CONTINUE;
     }
 
     // ------------------------------------------------------- Protected Methods
diff --git a/web/src/test/java/org/apache/commons/chain2/web/portlet/PortletGetLocaleCommandTestCase.java b/web/src/test/java/org/apache/commons/chain2/web/portlet/PortletGetLocaleCommandTestCase.java
index 8a1ec25..1ec9d13 100644
--- a/web/src/test/java/org/apache/commons/chain2/web/portlet/PortletGetLocaleCommandTestCase.java
+++ b/web/src/test/java/org/apache/commons/chain2/web/portlet/PortletGetLocaleCommandTestCase.java
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.chain2.web.portlet;
 
+import org.apache.commons.chain2.Processing;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -125,8 +126,8 @@
         assertNotNull(localeKey);
         Object value = context.get(localeKey);
         assertNull(value);
-        boolean result = command.execute(context);
-        assertFalse(result);
+        Processing result = command.execute(context);
+        assertEquals(Processing.CONTINUE, result);
         value = context.get(localeKey);
         assertNotNull(value);
         assertTrue(value instanceof Locale);
diff --git a/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletGetLocaleCommandTestCase.java b/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletGetLocaleCommandTestCase.java
index e03631f..888a0e7 100644
--- a/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletGetLocaleCommandTestCase.java
+++ b/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletGetLocaleCommandTestCase.java
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.chain2.web.servlet;
 
+import org.apache.commons.chain2.Processing;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -128,8 +129,8 @@
     assertNotNull(localeKey);
     Object value = context.get(localeKey);
     assertNull(value);
-    boolean result = command.execute(context);
-    assertFalse(result);
+    Processing result = command.execute(context);
+    assertEquals(Processing.CONTINUE, result);
     value = context.get(localeKey);
     assertNotNull(value);
     assertTrue(value instanceof Locale);
diff --git a/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletSetLocaleCommandTestCase.java b/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletSetLocaleCommandTestCase.java
index 50eb884..d6564de 100644
--- a/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletSetLocaleCommandTestCase.java
+++ b/web/src/test/java/org/apache/commons/chain2/web/servlet/ServletSetLocaleCommandTestCase.java
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.chain2.web.servlet;
 
+import org.apache.commons.chain2.Processing;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -130,8 +131,8 @@
         context.put(localeKey, locale);
         assertNotNull(context.get(localeKey));
         assertNull(response.getLocale());
-        boolean result = command.execute(context);
-        assertFalse(result);
+        Processing result = command.execute(context);
+        assertEquals(Processing.CONTINUE, result);
         assertNotNull(response.getLocale());
         assertEquals(locale, response.getLocale());
     }