craigmcc 01/08/26 22:57:26
Modified: workflow/src/java/org/apache/commons/workflow Context.java
workflow/src/java/org/apache/commons/workflow/base
BaseContext.java
workflow/src/java/org/apache/commons/workflow/core
CoreRuleSet.java
workflow/src/test/org/apache/commons/workflow/core
CoreExecuteTestCase.java
Added: workflow/src/java/org/apache/commons/workflow/core
CallStep.java
Log:
Add the ability to call nested Activities, and an appropriate <core:call>
Step definition to trigger it. This Step assumes that the Activity to be
called has been pushed onto the top of the evaluation stack, but it makes
*no* assumpations about how the Activity got there.
Revision Changes Path
1.7 +39 -23
jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/Context.java
Index: Context.java
===================================================================
RCS file:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/Context.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- Context.java 2001/08/23 03:52:17 1.6
+++ Context.java 2001/08/27 05:57:25 1.7
@@ -1,7 +1,7 @@
/*
- * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/Context.java,v
1.6 2001/08/23 03:52:17 craigmcc Exp $
- * $Revision: 1.6 $
- * $Date: 2001/08/23 03:52:17 $
+ * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/Context.java,v
1.7 2001/08/27 05:57:25 craigmcc Exp $
+ * $Revision: 1.7 $
+ * $Date: 2001/08/27 05:57:25 $
*
* ====================================================================
*
@@ -88,7 +88,7 @@
* the Step implementations that are executed to maintain the stack's
* integrity.</p>
*
- * @version $Revision: 1.6 $ $Date: 2001/08/23 03:52:17 $
+ * @version $Revision: 1.7 $ $Date: 2001/08/27 05:57:25 $
* @author Craig R. McClanahan
*/
@@ -317,9 +317,24 @@
/**
+ * <p>Save the execution state (i.e. the currently assigned next step)
+ * of the Activity we are currently executing, and begin executing the
+ * specified Activity. When that Activity exits (either normally
+ * or by throwing an exception), the previous Activity will be resumed
+ * where it left off.
+ *
+ * @param activity The Activity to be called
+ */
+ public void call(Activity activity);
+
+
+ /**
* <p>Execute the <code>Step</code> currently identified as the next
* step, and continue execution until there is no next step, or until
- * the <code>suspend</code> property has been set to true.
+ * the <code>suspend</code> property has been set to true. Upon
+ * completion of an activity, any execution state that was saved to due
+ * to utilizing the <code>call()</code> method will be restored, and
+ * the saved Activity execution shall be resumed.
*
* @exception StepException if an exception is thrown by the
* <code>execute()</code> method of a Step we have executed
@@ -345,27 +360,16 @@
/**
- * <p>Set the <code>Activity</code> to be executed, and make the first
- * defined <code>Step</code> within this <code>Activity</code> the next
- * action to be performed by <code>execute()</code>.</p>
- *
- * <p>If <code>null</code> is passed, any currently associated Activity
- * will be released, and the evaluation stack will be cleared.</p>
- *
- * <p><strong>WARNING</strong> - This will have to become more sophisticated
- * in order to support calling nested Activities.</p>
- *
- * @param activity The new Activity to be executed, or <code>null</code>
- * to release resources
+ * <p>Return the <code>Step</code> that will be executed the next time
+ * that <code>execute()</code> is called, if any.</p>
*/
- public void setActivity(Activity activity);
+ public Step getNextStep();
/**
- * <p>Return the <code>Step</code> that will be executed the next time
- * that <code>execute()</code> is called, if any.</p>
+ * <p>Return the suspend flag.</p>
*/
- public Step getNextStep();
+ public boolean getSuspend();
/**
@@ -383,9 +387,21 @@
/**
- * <p>Return the suspend flag.</p>
+ * <p>Set the <code>Activity</code> to be executed, and make the first
+ * defined <code>Step</code> within this <code>Activity</code> the next
+ * action to be performed by <code>execute()</code>.</p>
+ *
+ * <p>If <code>null</code> is passed, any currently associated Activity
+ * will be released, and the evaluation stack and nested call state
+ * stack will be cleared.</p>
+ *
+ * <p><strong>WARNING</strong> - This will have to become more sophisticated
+ * in order to support calling nested Activities.</p>
+ *
+ * @param activity The new Activity to be executed, or <code>null</code>
+ * to release resources
*/
- public boolean getSuspend();
+ public void setActivity(Activity activity);
/**
1.10 +96 -29
jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/base/BaseContext.java
Index: BaseContext.java
===================================================================
RCS file:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/base/BaseContext.java,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- BaseContext.java 2001/08/23 03:52:18 1.9
+++ BaseContext.java 2001/08/27 05:57:26 1.10
@@ -1,7 +1,7 @@
/*
- * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/base/BaseContext.java,v
1.9 2001/08/23 03:52:18 craigmcc Exp $
- * $Revision: 1.9 $
- * $Date: 2001/08/23 03:52:18 $
+ * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/base/BaseContext.java,v
1.10 2001/08/27 05:57:26 craigmcc Exp $
+ * $Revision: 1.10 $
+ * $Date: 2001/08/27 05:57:26 $
*
* ====================================================================
*
@@ -84,7 +84,7 @@
* class. If it is used in a multiple thread environment, callers must
* take suitable precations.</p>
*
- * @version $Revision: 1.9 $ $Date: 2001/08/23 03:52:18 $
+ * @version $Revision: 1.10 $ $Date: 2001/08/27 05:57:26 $
* @author Craig R. McClanahan
*/
@@ -125,6 +125,13 @@
/**
+ * The suspended "next step" Step for each in-progress Activity that has
+ * issued a <code>call()</code> to execute a subordinate Activity.
+ */
+ protected ArrayStack calls = new ArrayStack();
+
+
+ /**
* The set of names associated with the registered <code>Scopes</code>.
*/
protected String names[] = new String[MAX_SCOPES];
@@ -496,9 +503,36 @@
/**
+ * <p>Save the execution state (i.e. the currently assigned next step)
+ * of the Activity we are currently executing, and begin executing the
+ * specified Activity. When that Activity exits (either normally
+ * or by throwing an exception), the previous Activity will be resumed
+ * where it left off.
+ *
+ * @param activity The Activity to be called
+ */
+ public void call(Activity activity) {
+
+ // Save the next Step for the current Activity (if we have
+ // any remaining steps to worry about -- a call on the last
+ // step of an activity is more like a non-local goto)
+ if (this.nextStep != null)
+ calls.push(this.nextStep);
+
+ // Forward control to the first Step of the new Activity
+ this.activity = activity;
+ this.nextStep = activity.getFirstStep();
+
+ }
+
+
+ /**
* <p>Execute the <code>Step</code> currently identified as the next
* step, and continue execution until there is no next step, or until
- * the <code>suspend</code> property has been set to true.
+ * the <code>suspend</code> property has been set to true. Upon
+ * completion of an activity, any execution state that was saved to due
+ * to utilizing the <code>call()</code> method will be restored, and
+ * the saved Activity execution shall be resumed.
*
* @exception StepException if an exception is thrown by the
* <code>execute()</code> method of a Step we have executed
@@ -527,11 +561,29 @@
StepException exception = null;
while (true) {
+ // Process a suspension of Activity execution
if (suspend)
break; // Suspend set by a Step
- if (nextStep == null)
- break; // We have completed this Activity
+ // Process completion of an Activity
+ if (nextStep == null) {
+
+ // If there are no active calls, we are done
+ if (calls.empty())
+ break;
+
+ // If there are active calls, resume the most recent one
+ try {
+ nextStep = (Step) calls.pop();
+ this.activity = nextStep.getActivity();
+ } catch (EmptyStackException e) {
+ ; // Can not happen
+ }
+ continue;
+
+ }
+
+ // Execute the (now) current Step
thisStep = nextStep;
nextStep = thisStep.getNextStep();
try {
@@ -572,6 +624,19 @@
/**
+ * Return the set of pending Step executions that are pending because
+ * of calls to subordinate Activities have occurred. If there are
+ * no pending Step executions, a zero-length array is returned.
+ */
+ public Step[] getCalls() {
+
+ Step steps[] = new Step[calls.size()];
+ return ((Step[]) calls.toArray(steps));
+
+ }
+
+
+ /**
* Return the JXPathContext object that represents a unified namespace
* covering all of our registered <code>Scopes</code>.
*/
@@ -585,12 +650,34 @@
/**
+ * <p>Return the <code>Step</code> that will be executed the next time
+ * that <code>execute()</code> is called, if any.</p>
+ */
+ public Step getNextStep() {
+
+ return (this.nextStep);
+
+ }
+
+
+ /**
+ * <p>Return the suspend flag.</p>
+ */
+ public boolean getSuspend() {
+
+ return (this.suspend);
+
+ }
+
+
+ /**
* <p>Set the <code>Activity</code> to be executed, and make the first
* defined <code>Step</code> within this <code>Activity</code> the next
* action to be performed by <code>execute()</code>.</p>
*
* <p>If <code>null</code> is passed, any currently associated Activity
- * will be released, and the evaluation stack will be cleared.</p>
+ * will be released, and the evaluation stack and nested call state
+ * stack will be cleared.</p>
*
* <p><strong>WARNING</strong> - This will have to become more sophisticated
* in order to support calling nested Activities.</p>
@@ -608,22 +695,12 @@
this.activity = activity;
this.nextStep = activity.getFirstStep();
}
+ calls.clear();
}
/**
- * <p>Return the <code>Step</code> that will be executed the next time
- * that <code>execute()</code> is called, if any.</p>
- */
- public Step getNextStep() {
-
- return (this.nextStep);
-
- }
-
-
- /**
* <p>Set the <code>Step</code> that will be executed the next time
* that <code>execute()</code> is called. This is called by a
* <code>Step</code> that wants to perform branching based on some
@@ -641,16 +718,6 @@
throw new IllegalArgumentException
("Step is not part of the current Activity");
this.nextStep = nextStep;
-
- }
-
-
- /**
- * <p>Return the suspend flag.</p>
- */
- public boolean getSuspend() {
-
- return (this.suspend);
}
1.3 +13 -4
jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/core/CoreRuleSet.java
Index: CoreRuleSet.java
===================================================================
RCS file:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/core/CoreRuleSet.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- CoreRuleSet.java 2001/08/27 02:55:54 1.2
+++ CoreRuleSet.java 2001/08/27 05:57:26 1.3
@@ -1,7 +1,7 @@
/*
- * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/core/CoreRuleSet.java,v
1.2 2001/08/27 02:55:54 craigmcc Exp $
- * $Revision: 1.2 $
- * $Date: 2001/08/27 02:55:54 $
+ * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/core/CoreRuleSet.java,v
1.3 2001/08/27 05:57:26 craigmcc Exp $
+ * $Revision: 1.3 $
+ * $Date: 2001/08/27 05:57:26 $
*
* ====================================================================
*
@@ -76,7 +76,7 @@
* </pre>
*
* @author Craig R. McClanahan
- * @version $Revision: 1.2 $ $Date: 2001/08/27 02:55:54 $
+ * @version $Revision: 1.3 $ $Date: 2001/08/27 05:57:26 $
*/
public class CoreRuleSet extends BaseRuleSet {
@@ -127,6 +127,15 @@
digester.addSetNext
(prefix + "and/descriptor", "addDescriptor",
"org.apache.commons.workflow.Descriptor");
+
+ digester.addObjectCreate
+ (prefix + "call",
+ "org.apache.commons.workflow.core.CallStep");
+ digester.addSetProperties
+ (prefix + "call");
+ digester.addSetNext
+ (prefix + "call", "addStep",
+ "org.apache.commons.workflow.Step");
digester.addObjectCreate
(prefix + "duplicate",
1.1
jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/core/CallStep.java
Index: CallStep.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/java/org/apache/commons/workflow/core/CallStep.java,v
1.1 2001/08/27 05:57:26 craigmcc Exp $
* $Revision: 1.1 $
* $Date: 2001/08/27 05:57:26 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.workflow.core;
import java.util.EmptyStackException;
import org.apache.commons.workflow.Activity;
import org.apache.commons.workflow.Context;
import org.apache.commons.workflow.StepException;
import org.apache.commons.workflow.base.BaseStep;
/**
* <p>Pop the top value from the evaluation stack, which must be an
* <code>Activity</code>, and initiate a "subroutine call" to execute
* this Activity before resuming the current one.</p>
*
* <p><strong>NOTE</strong> - The means by which the Activity on the top
* of the stack was acquired is NOT mandated by this Step implementation.</p>
*
* @version $Revision: 1.1 $ $Date: 2001/08/27 05:57:26 $
* @author Craig R. McClanahan
*/
public class CallStep extends BaseStep {
// ----------------------------------------------------------= Constructors
/**
* Construct a default instance of this Step.
*/
public CallStep() {
super();
}
/**
* Construct an instance of this Step with the specified identifier.
*
* @param id Step identifier
*/
public CallStep(String id) {
super();
setId(id);
}
// --------------------------------------------------------- Public Methods
/**
* Perform the executable actions related to this Step, in the context of
* the specified Context.
*
* @param context The Context that is tracking our execution state
*
* @exception StepException if a processing error has occurred
*/
public void execute(Context context) throws StepException {
// Pop the evaluation stack
Object value = null;
try {
value = context.pop();
} catch (EmptyStackException e) {
throw new StepException("Evaluation stack is empty", e, this);
}
// Call the requested Activity
if (!(value instanceof Activity))
throw new StepException("Top of stack is not an Activity", this);
context.call((Activity) value);
}
}
1.6 +94 -4
jakarta-commons-sandbox/workflow/src/test/org/apache/commons/workflow/core/CoreExecuteTestCase.java
Index: CoreExecuteTestCase.java
===================================================================
RCS file:
/home/cvs/jakarta-commons-sandbox/workflow/src/test/org/apache/commons/workflow/core/CoreExecuteTestCase.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- CoreExecuteTestCase.java 2001/08/26 03:38:11 1.5
+++ CoreExecuteTestCase.java 2001/08/27 05:57:26 1.6
@@ -1,7 +1,7 @@
/*
- * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/test/org/apache/commons/workflow/core/CoreExecuteTestCase.java,v
1.5 2001/08/26 03:38:11 craigmcc Exp $
- * $Revision: 1.5 $
- * $Date: 2001/08/26 03:38:11 $
+ * $Header:
/home/cvs/jakarta-commons-sandbox/workflow/src/test/org/apache/commons/workflow/core/CoreExecuteTestCase.java,v
1.6 2001/08/27 05:57:26 craigmcc Exp $
+ * $Revision: 1.6 $
+ * $Date: 2001/08/27 05:57:26 $
*
* ====================================================================
*
@@ -85,7 +85,7 @@
* implementations.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 1.5 $ $Date: 2001/08/26 03:38:11 $
+ * @version $Revision: 1.6 $ $Date: 2001/08/27 05:57:26 $
*/
public class CoreExecuteTestCase extends TestCase
@@ -166,6 +166,96 @@
// ------------------------------------------------ Individual Test Methods
+
+
+ /**
+ * Call a nested Activity.
+ */
+ public void testCallNested() {
+
+ // Configure the steps in this activity
+ activity.addStep(new StringStep("01", "Original activity"));
+ activity.addStep(new SuspendStep("02"));
+ // Manually push a nested activity here
+ activity.addStep(new CallStep("03"));
+ /*
+ activity.addStep(new DuplicateStep("04"));
+ activity.addStep(new PopStep("05"));
+ */
+
+ // Execute the activity and validate results #1
+ try {
+ context.execute();
+ assertEquals("Trail contents",
+ "beforeActivity()/" +
+ "beforeStep(01)/afterStep(01)/" +
+ "beforeStep(02)/afterStep(02)/" +
+ "afterActivity()/",
+ trail.toString());
+ assertTrue("Stack is not empty",
+ !context.isEmpty());
+ assertEquals("Top of stack string",
+ "Original activity",
+ (String) context.peek());
+ assertTrue("Context was suspended",
+ context.getSuspend());
+ } catch (StepException e) {
+ e.printStackTrace(System.out);
+ if (e.getCause() != null) {
+ System.out.println("ROOT CAUSE");
+ e.getCause().printStackTrace(System.out);
+ }
+ fail("Threw StepException " + e);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ fail("Threw exception " + e);
+ }
+
+ // Manually push a nested Activity onto the evaluation stack
+ Activity nested = new BaseActivity();
+ nested.addStep(new StringStep("11", "Nested activity"));
+ nested.addStep(new SwapStep("12"));
+ context.push(nested);
+
+ // Execute the activity and validate results #2
+ try {
+ context.execute();
+ assertEquals("Trail contents",
+ "beforeActivity()/" +
+ "beforeStep(03)/afterStep(03)/" +
+ "beforeStep(11)/afterStep(11)/" +
+ "beforeStep(12)/afterStep(12)/" +
+ /*
+ "beforeStep(04)/afterStep(04)/" +
+ "beforeStep(05)/afterStep(05)/" +
+ */
+ "afterActivity()/",
+ trail.toString());
+ assertTrue("Stack is not empty",
+ !context.isEmpty());
+ assertEquals("Top of stack string",
+ "Original activity",
+ (String) context.pop());
+ assertEquals("Bottom of stack string",
+ "Nested activity",
+ (String) context.pop());
+ assertTrue("Stack is empty",
+ context.isEmpty());
+ assertTrue("Context was not suspended",
+ !context.getSuspend());
+ } catch (StepException e) {
+ e.printStackTrace(System.out);
+ if (e.getCause() != null) {
+ System.out.println("ROOT CAUSE");
+ e.getCause().printStackTrace(System.out);
+ }
+ fail("Threw StepException " + e);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ fail("Threw exception " + e);
+ }
+
+ }
/**