This is an automated email from the ASF dual-hosted git repository. juanpablo pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/jspwiki.git
commit 01ee8a5d43b529dcd5cd98bd17a4e0e13c271ba8 Author: juanpablo <[email protected]> AuthorDate: Sat May 2 19:41:30 2020 +0200 Break class cycles between Workflow and Step in order to prepare for JSPWIKI-304 (Workflows are not Serializable) --- .../wiki/tasks/auth/SaveUserProfileTask.java | 2 +- .../wiki/tasks/pages/PreSaveWikiPageTask.java | 12 +-- .../apache/wiki/tasks/pages/SaveWikiPageTask.java | 4 +- .../org/apache/wiki/workflow/AbstractStep.java | 60 ++++++-------- .../java/org/apache/wiki/workflow/Decision.java | 17 ++-- .../wiki/workflow/DefaultWorkflowManager.java | 45 +++++----- .../org/apache/wiki/workflow/SimpleDecision.java | 27 +++--- .../apache/wiki/workflow/SimpleNotification.java | 53 +++++------- .../main/java/org/apache/wiki/workflow/Step.java | 29 ++----- .../main/java/org/apache/wiki/workflow/Task.java | 74 +++++++---------- .../java/org/apache/wiki/workflow/Workflow.java | 21 ++++- .../org/apache/wiki/workflow/WorkflowBuilder.java | 8 +- .../apache/wiki/workflow/WorkflowEventEmitter.java | 13 +++ .../java/org/apache/wiki/auth/UserManagerTest.java | 4 +- .../apache/wiki/workflow/ApprovalWorkflowTest.java | 95 +++++++++------------- .../apache/wiki/workflow/DecisionQueueTest.java | 51 ++++++------ .../apache/wiki/workflow/SimpleDecisionTest.java | 8 +- .../java/org/apache/wiki/workflow/TaskTest.java | 10 +-- .../apache/wiki/workflow/WorkflowManagerTest.java | 13 ++- .../org/apache/wiki/workflow/WorkflowTest.java | 9 +- 20 files changed, 244 insertions(+), 311 deletions(-) diff --git a/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java b/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java index 3cb09a0..18325b3 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/tasks/auth/SaveUserProfileTask.java @@ -48,7 +48,7 @@ public class SaveUserProfileTask extends Task { @Override public Outcome execute() throws WikiException { // Retrieve user profile - final UserProfile profile = ( UserProfile )getWorkflow().getAttribute( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ); + final UserProfile profile = ( UserProfile )getWorkflowContext().get( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ); // Save the profile (userdatabase will take care of timestamps for us) m_engine.getManager( UserManager.class ).getUserDatabase().save( profile ); diff --git a/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/PreSaveWikiPageTask.java b/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/PreSaveWikiPageTask.java index c2ed0ac..5e65649 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/PreSaveWikiPageTask.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/PreSaveWikiPageTask.java @@ -19,14 +19,12 @@ package org.apache.wiki.tasks.pages; import org.apache.wiki.api.core.Context; -import org.apache.wiki.api.core.Engine; import org.apache.wiki.api.core.Page; import org.apache.wiki.api.exceptions.WikiException; import org.apache.wiki.filters.FilterManager; import org.apache.wiki.tasks.TasksManager; import org.apache.wiki.workflow.Outcome; import org.apache.wiki.workflow.Task; -import org.apache.wiki.workflow.Workflow; import org.apache.wiki.workflow.WorkflowManager; import java.security.Principal; @@ -60,10 +58,6 @@ public class PreSaveWikiPageTask extends Task { */ @Override public Outcome execute() throws WikiException { - // Retrieve attributes - final Engine engine = m_context.getEngine(); - final Workflow workflow = getWorkflow(); - // Get the wiki page final Page page = m_context.getPage(); @@ -76,11 +70,11 @@ public class PreSaveWikiPageTask extends Task { } // Run the pre-save filters. If any exceptions, add error to list, abort, and redirect - final String saveText = engine.getManager( FilterManager.class ).doPreSaveFiltering(m_context, m_proposedText); + final String saveText = m_context.getEngine().getManager( FilterManager.class ).doPreSaveFiltering(m_context, m_proposedText); // Stash the wiki context, old and new text as workflow attributes - workflow.setAttribute( WorkflowManager.WF_WP_SAVE_ATTR_PRESAVE_WIKI_CONTEXT, m_context ); - workflow.setAttribute( WorkflowManager.WF_WP_SAVE_FACT_PROPOSED_TEXT, saveText ); + getWorkflowContext().put( WorkflowManager.WF_WP_SAVE_ATTR_PRESAVE_WIKI_CONTEXT, m_context ); + getWorkflowContext().put( WorkflowManager.WF_WP_SAVE_FACT_PROPOSED_TEXT, saveText ); return Outcome.STEP_COMPLETE; } diff --git a/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/SaveWikiPageTask.java b/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/SaveWikiPageTask.java index 3f83d08..0063c40 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/SaveWikiPageTask.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/tasks/pages/SaveWikiPageTask.java @@ -51,8 +51,8 @@ public class SaveWikiPageTask extends Task { @Override public Outcome execute() throws WikiException { // Retrieve attributes - final Context context = ( Context ) getWorkflow().getAttribute( WorkflowManager.WF_WP_SAVE_ATTR_PRESAVE_WIKI_CONTEXT ); - final String proposedText = (String) getWorkflow().getAttribute( WorkflowManager.WF_WP_SAVE_FACT_PROPOSED_TEXT ); + final Context context = ( Context )getWorkflowContext().get( WorkflowManager.WF_WP_SAVE_ATTR_PRESAVE_WIKI_CONTEXT ); + final String proposedText = ( String )getWorkflowContext().get( WorkflowManager.WF_WP_SAVE_FACT_PROPOSED_TEXT ); final Engine engine = context.getEngine(); final Page page = context.getPage(); diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/AbstractStep.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/AbstractStep.java index e545990..414aa20 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/AbstractStep.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/AbstractStep.java @@ -20,7 +20,6 @@ package org.apache.wiki.workflow; import org.apache.wiki.api.exceptions.WikiException; -import java.io.Serializable; import java.security.Principal; import java.util.ArrayList; import java.util.Collection; @@ -53,7 +52,10 @@ public abstract class AbstractStep implements Step { private final Map< Outcome, Step > m_successors; - private Workflow m_workflow; + private int workflowId; + + /** attribute map. */ + private Map< String, Object > workflowContext; private Outcome m_outcome; @@ -63,7 +65,7 @@ public abstract class AbstractStep implements Step { /** * Protected constructor that creates a new Step with a specified message key. After construction, the method - * {@link #setWorkflow(Workflow)} should be called. + * {@link #setWorkflow(int, Map)} should be called. * * @param messageKey the Step's message key, such as {@code decision.editPageApproval}. By convention, the message prefix should * be a lower-case version of the Step's type, plus a period (<em>e.g.</em>, {@code task.} and {@code decision.}). @@ -82,13 +84,14 @@ public abstract class AbstractStep implements Step { /** * Constructs a new Step belonging to a specified Workflow and having a specified message key. * - * @param workflow the workflow the Step belongs to + * @param workflowId the parent workflow id to set + * @param workflowContext the parent workflow context to set * @param messageKey the Step's message key, such as {@code decision.editPageApproval}. By convention, the message prefix should * be a lower-case version of the Step's type, plus a period (<em>e.g.</em>, {@code task.} and {@code decision.}). */ - public AbstractStep( final Workflow workflow, final String messageKey ) { + public AbstractStep( final int workflowId, final Map< String, Object > workflowContext, final String messageKey ) { this( messageKey ); - setWorkflow( workflow ); + setWorkflow( workflowId, workflowContext ); } /** @@ -133,16 +136,6 @@ public abstract class AbstractStep implements Step { /** * {@inheritDoc} */ - public final Serializable[] getMessageArguments() { - if( m_workflow == null ) { - return new Serializable[ 0 ]; - } - return m_workflow.getMessageArguments(); - } - - /** - * {@inheritDoc} - */ public final String getMessageKey() { return m_key; } @@ -157,16 +150,6 @@ public abstract class AbstractStep implements Step { /** * {@inheritDoc} */ - public Principal getOwner() { - if( m_workflow == null ) { - return null; - } - return m_workflow.getOwner(); - } - - /** - * {@inheritDoc} - */ public final Date getStartTime() { return m_start; } @@ -174,13 +157,6 @@ public abstract class AbstractStep implements Step { /** * {@inheritDoc} */ - public final synchronized Workflow getWorkflow() { - return m_workflow; - } - - /** - * {@inheritDoc} - */ public final boolean isCompleted() { return m_completed; } @@ -235,12 +211,22 @@ public abstract class AbstractStep implements Step { // --------------------------Helper methods-------------------------- /** - * method that sets the parent Workflow post-construction. + * method that sets the parent Workflow id and context post-construction. * - * @param workflow the parent workflow to set + * @param workflowId the parent workflow id to set + * @param workflowContext the parent workflow context to set */ - public final synchronized void setWorkflow( final Workflow workflow ) { - m_workflow = workflow; + public final synchronized void setWorkflow( final int workflowId, final Map< String, Object > workflowContext ) { + this.workflowId = workflowId; + this.workflowContext = workflowContext; + } + + public int getWorkflowId() { + return workflowId; + } + + public Map< String, Object > getWorkflowContext() { + return workflowContext; } /** diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Decision.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Decision.java index dbbabed..07749c1 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Decision.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Decision.java @@ -25,6 +25,7 @@ import java.security.Principal; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; /** @@ -61,13 +62,14 @@ public abstract class Decision extends AbstractStep { /** * Constructs a new Decision for a required "actor" Principal, having a default Outcome. * - * @param workflow the parent Workflow object + * @param workflowId the parent workflow id to set + * @param workflowContext the parent workflow context to set * @param messageKey the i18n message key that represents the message the actor will see * @param actor the Principal (<em>e.g.</em>, a WikiPrincipal, Role, GroupPrincipal) who is required to select an appropriate Outcome * @param defaultOutcome the Outcome that the user interface will recommend as the default choice */ - public Decision( final Workflow workflow, final String messageKey, final Principal actor, final Outcome defaultOutcome ) { - super( workflow, messageKey ); + public Decision( final int workflowId, final Map< String, Object > workflowContext, final String messageKey, final Principal actor, final Outcome defaultOutcome ) { + super( workflowId, workflowContext, messageKey ); m_actor = actor; m_defaultOutcome = defaultOutcome; m_facts = new ArrayList<>(); @@ -104,14 +106,7 @@ public abstract class Decision extends AbstractStep { */ public void decide( final Outcome outcome ) throws WikiException { super.setOutcome( outcome ); - - // If current workflow is waiting for input, restart it and remove Decision from DecisionQueue - final Workflow w = getWorkflow(); - if( w.getCurrentState() == Workflow.WAITING && this.equals( w.getCurrentStep() ) ) { - WorkflowEventEmitter.fireEvent( this, WorkflowEvent.DQ_REMOVAL ); - // Restart workflow - w.restart(); - } + WorkflowEventEmitter.fireEvent( this, WorkflowEvent.DQ_REMOVAL ); } /** diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java index d837611..723b5c0 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java @@ -18,19 +18,20 @@ */ package org.apache.wiki.workflow; +import org.apache.log4j.Logger; import org.apache.wiki.api.core.Engine; import org.apache.wiki.api.core.Session; import org.apache.wiki.api.exceptions.WikiException; import org.apache.wiki.auth.AuthorizationManager; import org.apache.wiki.auth.acl.UnresolvedPrincipal; import org.apache.wiki.event.WikiEvent; -import org.apache.wiki.event.WikiEventManager; import org.apache.wiki.event.WorkflowEvent; import java.security.Principal; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -45,6 +46,8 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class DefaultWorkflowManager implements WorkflowManager { + private static final Logger LOG = Logger.getLogger( DefaultWorkflowManager.class ); + private final DecisionQueue m_queue = new DecisionQueue(); private final Set< Workflow > m_workflows; private final Map< String, Principal > m_approvers; @@ -52,15 +55,13 @@ public class DefaultWorkflowManager implements WorkflowManager { private Engine m_engine = null; /** - * Constructs a new WorkflowManager, with an empty workflow cache. New Workflows are automatically assigned unique identifiers, - * starting with 1. + * Constructs a new WorkflowManager, with an empty workflow cache. */ public DefaultWorkflowManager() { - m_next = 1; m_workflows = ConcurrentHashMap.newKeySet(); m_approvers = new ConcurrentHashMap<>(); m_completed = new CopyOnWriteArrayList<>(); - WikiEventManager.addWikiEventListener( WorkflowEventEmitter.get(), this ); + WorkflowEventEmitter.registerListener( this ); } /** @@ -68,8 +69,6 @@ public class DefaultWorkflowManager implements WorkflowManager { */ @Override public void start( final Workflow workflow ) throws WikiException { - m_workflows.add( workflow ); - workflow.setId( nextId() ); workflow.start(); } @@ -176,19 +175,6 @@ public class DefaultWorkflowManager implements WorkflowManager { return m_queue; } - private volatile int m_next; - - /** - * Returns the next available unique identifier, which is subsequently incremented. - * - * @return the id - */ - private synchronized int nextId() { - final int current = m_next; - m_next++; - return current; - } - /** * {@inheritDoc} */ @@ -251,9 +237,6 @@ public class DefaultWorkflowManager implements WorkflowManager { * @param workflow the workflow to add */ protected void add( final Workflow workflow ) { - if ( workflow.getId() == Workflow.ID_NOT_SET ) { - workflow.setId( nextId() ); - } m_workflows.add( workflow ); } @@ -271,7 +254,21 @@ public class DefaultWorkflowManager implements WorkflowManager { } protected void removeFromDecisionQueue( final Decision decision ) { - getDecisionQueue().remove( decision ); + // If current workflow is waiting for input, restart it and remove Decision from DecisionQueue + final int workflowId = decision.getWorkflowId(); + final Optional< Workflow > optw = m_workflows.stream().filter( w -> w.getId() == workflowId ).findAny(); + if( optw.isPresent() ) { + final Workflow w = optw.get(); + if( w.getCurrentState() == Workflow.WAITING && decision.equals( w.getCurrentStep() ) ) { + getDecisionQueue().remove( decision ); + // Restart workflow + try { + w.restart(); + } catch( final WikiException e ) { + LOG.error( "restarting workflow #" + w.getId() + " caused " + e.getMessage(), e ); + } + } + } } protected void addToDecisionQueue( final Decision decision ) { diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleDecision.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleDecision.java index 181c355..bae296a 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleDecision.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleDecision.java @@ -19,30 +19,27 @@ package org.apache.wiki.workflow; import java.security.Principal; +import java.util.Map; + /** - * Decision subclass that includes two available Outcomes: - * {@link Outcome#DECISION_APPROVE} or {@link Outcome#DECISION_DENY}. - * The Decision is reassignable, and the default Outcome is - * {@link Outcome#DECISION_APPROVE}. - * + * Decision subclass that includes two available Outcomes: {@link Outcome#DECISION_APPROVE} or {@link Outcome#DECISION_DENY}. + * The Decision is reassignable, and the default Outcome is {@link Outcome#DECISION_APPROVE}. */ -public class SimpleDecision extends Decision -{ +public class SimpleDecision extends Decision { private static final long serialVersionUID = 8192213077644617341L; /** * Constructs a new SimpleDecision assigned to a specified actor. - * @param workflow the parent Workflow - * @param messageKey the message key that describes the Decision, which - * will be presented in the UI - * @param actor the Principal (<em>e.g.</em>, WikiPrincipal, - * GroupPrincipal, Role) who will decide + * + * @param workflowId the parent workflow id to set + * @param workflowContext the parent workflow context to set + * @param messageKey the message key that describes the Decision, which will be presented in the UI + * @param actor the Principal (<em>e.g.</em>, WikiPrincipal, GroupPrincipal, Role) who will decide */ - public SimpleDecision( Workflow workflow, String messageKey, Principal actor ) - { - super( workflow, messageKey, actor, Outcome.DECISION_APPROVE ); + public SimpleDecision( final int workflowId, final Map< String, Object > workflowContext, final String messageKey, final Principal actor ) { + super( workflowId, workflowContext, messageKey, actor, Outcome.DECISION_APPROVE ); // Add the other default outcomes super.addSuccessor( Outcome.DECISION_DENY, null ); diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleNotification.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleNotification.java index d2cfdb0..44d88a2 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleNotification.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/SimpleNotification.java @@ -18,59 +18,50 @@ */ package org.apache.wiki.workflow; -import java.security.Principal; - import org.apache.wiki.api.exceptions.WikiException; +import java.security.Principal; +import java.util.Map; + /** - * Decision subclass used for notifications that includes only one available Outcome: - * {@link Outcome#DECISION_ACKNOWLEDGE}. The Decision is not reassignable, and - * the default Outcome is {@link Outcome#DECISION_ACKNOWLEDGE}. + * Decision subclass used for notifications that includes only one available Outcome: {@link Outcome#DECISION_ACKNOWLEDGE}. The Decision is + * not reassignable, and the default Outcome is {@link Outcome#DECISION_ACKNOWLEDGE}. * * @since 2.5 */ -public final class SimpleNotification extends Decision -{ +public final class SimpleNotification extends Decision { private static final long serialVersionUID = -3392947495169819527L; /** - * Constructs a new SimpleNotification object with a supplied message key, - * associated Workflow, and named actor who must acknowledge the message. - * The notification is placed in the Principal's list of queued Decisions. - * Because the only available Outcome is - * {@link Outcome#DECISION_ACKNOWLEDGE}, the actor can only acknowledge the - * message. + * Constructs a new SimpleNotification object with a supplied message key, associated Workflow, and named actor who must acknowledge + * the message. The notification is placed in the Principal's list of queued Decisions. Because the only available Outcome is + * {@link Outcome#DECISION_ACKNOWLEDGE}, the actor can only acknowledge the message. * - * @param workflow - * the Workflow to associate this notification with - * @param messageKey - * the message key - * @param actor - * the Principal who will acknowledge the message + * @param workflowId the parent workflow id to set + * @param workflowContext the parent workflow context to set + * @param messageKey the message key + * @param actor the Principal who will acknowledge the message */ - public SimpleNotification( Workflow workflow, String messageKey, Principal actor ) - { - super( workflow, messageKey, actor, Outcome.DECISION_ACKNOWLEDGE ); + public SimpleNotification( final int workflowId, final Map< String, Object > workflowContext, final String messageKey, final Principal actor ) { + super( workflowId, workflowContext, messageKey, actor, Outcome.DECISION_ACKNOWLEDGE ); } /** - * Convenience method that simply calls {@link #decide(Outcome)} - * with the value {@link Outcome#DECISION_ACKNOWLEDGE}. + * Convenience method that simply calls {@link #decide(Outcome)} with the value {@link Outcome#DECISION_ACKNOWLEDGE}. + * * @throws WikiException never */ - public void acknowledge() throws WikiException - { + public void acknowledge() throws WikiException { this.decide( Outcome.DECISION_ACKNOWLEDGE ); } /** - * Notifications cannot be re-assigned, so this method always returns - * <code>false</code>. - * @return <code>false</code> always + * Notifications cannot be re-assigned, so this method always returns <code>false</code>. + * + * @return <code>false</code> always. */ - public boolean isReassignable() - { + public boolean isReassignable() { return false; } diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Step.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Step.java index 50e3327..5d890cc 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Step.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Step.java @@ -25,6 +25,8 @@ import java.security.Principal; import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Map; + /** * <p> @@ -124,14 +126,6 @@ public interface Step extends Serializable { String getMessageKey(); /** - * Returns the message arguments for this Step, typically by delegating to the parent Workflow's {@link Workflow#getMessageArguments()} - * method. - * - * @return the message arguments. - */ - Serializable[] getMessageArguments(); - - /** * Returns the Outcome of this Step's processing; by default, {@link Outcome#STEP_CONTINUE}. * * @return the outcome @@ -181,13 +175,6 @@ public interface Step extends Serializable { void setOutcome( Outcome outcome ); /** - * Convenience method that returns the owner of the Workflow by delegating to {@link Workflow#getOwner()}. - * - * @return the owner of the Workflow - */ - Principal getOwner(); - - /** * Identifies the next Step for a particular Outcome; if there is no next Step for this Outcome, this method returns <code>null</code>. * * @param outcome the outcome @@ -196,17 +183,11 @@ public interface Step extends Serializable { Step getSuccessor( Outcome outcome ); /** - * Gets the Workflow that is the parent of this Step. - * - * @return the workflow - */ - Workflow getWorkflow(); - - /** * Sets the parent Workflow post-construction. Should be called after building a {@link Step}. * - * @param workflow the parent workflow to set + * @param workflowId the parent workflow id to set + * @param workflowContext the parent workflow context to set */ - void setWorkflow( Workflow workflow ); + void setWorkflow( final int workflowId, final Map< String, Object > workflowContext ); } diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Task.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Task.java index dd88086..5e560ee 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Task.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Task.java @@ -19,93 +19,77 @@ package org.apache.wiki.workflow; import java.security.Principal; +import java.util.Map; + /** - * AbstractStep subclass that executes instructions, uninterrupted, and results - * in an Outcome. Concrete classes only need to implement {@link Task#execute()}. - * When the execution step completes, <code>execute</code> must return - * {@link Outcome#STEP_COMPLETE}, {@link Outcome#STEP_CONTINUE} or - * {@link Outcome#STEP_ABORT}. Subclasses can add any errors by calling the - * helper method {@link AbstractStep#addError(String)}. The execute method should - * <em>generally</em> capture and add errors to the error list instead of + * AbstractStep subclass that executes instructions, uninterrupted, and results in an Outcome. Concrete classes only need to implement + * {@link Task#execute()}. When the execution step completes, <code>execute</code> must return {@link Outcome#STEP_COMPLETE}, + * {@link Outcome#STEP_CONTINUE} or {@link Outcome#STEP_ABORT}. Subclasses can add any errors by calling the helper method + * {@link AbstractStep#addError(String)}. The execute method should <em>generally</em> capture and add errors to the error list instead of * throwing a WikiException. * <p> * * @since 2.5 */ -public abstract class Task extends AbstractStep -{ +public abstract class Task extends AbstractStep { + private static final long serialVersionUID = 4630293957752430807L; - + private Step m_successor = null; /** - * Protected constructor that creates a new Task with a specified message key. - * After construction, the protected method {@link #setWorkflow(Workflow)} should be - * called. + * Public constructor that creates a new Task with a specified message key. After construction, the protected method + * {@link #setWorkflow(int, Map)} should be called. * - * @param messageKey - * the Step's message key, such as - * <code>decision.editPageApproval</code>. By convention, the - * message prefix should be a lower-case version of the Step's - * type, plus a period (<em>e.g.</em>, <code>task.</code> - * and <code>decision.</code>). + * @param messageKey the Step's message key, such as <code>decision.editPageApproval</code>. By convention, the message prefix should be + * a lower-case version of the Step's type, plus a period (<em>e.g.</em>, <code>task.</code> and <code>decision.</code>). */ - public Task( String messageKey ) - { + public Task( final String messageKey ) { super( messageKey ); super.addSuccessor( Outcome.STEP_COMPLETE, null ); super.addSuccessor( Outcome.STEP_ABORT, null ); } /** - * Constructs a new instance of a Task, with an associated Workflow and - * message key. + * Constructs a new instance of a Task, with an associated Workflow and message key. * - * @param workflow - * the associated workflow - * @param messageKey - * the i18n message key + * @param workflowId the parent workflow id to set + * @param workflowContext the parent workflow context to set + * @param messageKey the i18n message key */ - public Task( Workflow workflow, String messageKey ) - { + public Task( final int workflowId, final Map< String, Object > workflowContext, final String messageKey ) { this( messageKey ); - setWorkflow( workflow ); + setWorkflow( workflowId, workflowContext ); } /** * Returns {@link SystemPrincipal#SYSTEM_USER}. + * * @return the system principal */ - public final Principal getActor() - { + public final Principal getActor() { return SystemPrincipal.SYSTEM_USER; } /** - * Sets the successor Step to this one, which will be triggered if the Task - * completes successfully (that is, {@link Step#getOutcome()} returns - * {@link Outcome#STEP_COMPLETE}. This method is really a convenient - * shortcut for {@link Step#addSuccessor(Outcome, Step)}, where the first - * parameter is {@link Outcome#STEP_COMPLETE}. + * Sets the successor Step to this one, which will be triggered if the Task completes successfully (that is, {@link Step#getOutcome()} + * returns {@link Outcome#STEP_COMPLETE}. This method is really a convenient shortcut for {@link Step#addSuccessor(Outcome, Step)}, + * where the first parameter is {@link Outcome#STEP_COMPLETE}. * - * @param step - * the successor + * @param step the successor */ - public final synchronized void setSuccessor( Step step ) - { + public final synchronized void setSuccessor( final Step step ) { m_successor = step; } /** - * Identifies the next Step after this Task finishes successfully. This - * method will always return the value set in method + * Identifies the next Step after this Task finishes successfully. This method will always return the value set in method * {@link #setSuccessor(Step)}, regardless of the current completion state. * * @return the next step */ - public final synchronized Step getSuccessor() - { + public final synchronized Step getSuccessor() { return m_successor; } diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Workflow.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Workflow.java index 7657b27..42c6c2f 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/Workflow.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/Workflow.java @@ -30,6 +30,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; /** @@ -164,6 +165,8 @@ public class Workflow implements Serializable { private static final long serialVersionUID = 5228149040690660032L; + private static final AtomicInteger idsCounter = new AtomicInteger( 1 ); + /** ID value: the workflow ID has not been set. */ public static final int ID_NOT_SET = 0; @@ -217,12 +220,13 @@ public class Workflow implements Serializable { m_attributes = new ConcurrentHashMap<>(); m_currentStep = null; m_history = new LinkedList<>(); - m_id = ID_NOT_SET; + m_id = idsCounter.getAndIncrement(); m_key = messageKey; m_messageArgs = new ArrayList<>(); m_owner = owner; m_started = false; m_state = CREATED; + WorkflowEventEmitter.fireEvent( this, WorkflowEvent.CREATED ); } /** @@ -243,7 +247,7 @@ public class Workflow implements Serializable { if( m_currentStep != null ) { if( m_currentStep instanceof Decision ) { - WorkflowEventEmitter.fireEvent( ( Decision )m_currentStep, WorkflowEvent.DQ_REMOVAL ); + WorkflowEventEmitter.fireEvent( m_currentStep, WorkflowEvent.DQ_REMOVAL ); } m_currentStep.setOutcome( Outcome.STEP_ABORT ); m_history.addLast( m_currentStep ); @@ -313,6 +317,15 @@ public class Workflow implements Serializable { } /** + * Retrieves workflow's attributes. + * + * @return workflow's attributes. + */ + public final Map< String, Object > getAttributes() { + return m_attributes; + } + + /** * The end time for this Workflow, expressed as a system time number. This value is equal to the end-time value returned by the final * Step's {@link Step#getEndTime()} method, if the workflow has completed. Otherwise, this method returns {@link Step#TIME_NOT_SET}. * @@ -461,6 +474,7 @@ public class Workflow implements Serializable { if( m_state != WAITING ) { throw new IllegalStateException( "Workflow is not paused; cannot restart." ); } + WorkflowEventEmitter.fireEvent( this, WorkflowEvent.STARTED ); m_state = RUNNING; WorkflowEventEmitter.fireEvent( this, WorkflowEvent.RUNNING ); @@ -522,10 +536,11 @@ public class Workflow implements Serializable { if( m_started ) { throw new IllegalStateException( "Workflow has already started." ); } + WorkflowEventEmitter.fireEvent( this, WorkflowEvent.STARTED ); m_started = true; m_state = RUNNING; - WorkflowEventEmitter.fireEvent( this, WorkflowEvent.RUNNING ); + WorkflowEventEmitter.fireEvent( this, WorkflowEvent.RUNNING ); // Mark the first step as the current one & add to history m_currentStep = m_firstStep; m_history.add( m_currentStep ); diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowBuilder.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowBuilder.java index 9a8e7f2..b497147 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowBuilder.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowBuilder.java @@ -117,7 +117,7 @@ public final class WorkflowBuilder { if ( decisionRequired ) { // Look up the name of the approver (user or group) listed in jspwiki.properties; approvals go to the approver's decision queue final Principal approverPrincipal = mgr.getApprover( workflowApproverKey ); - final Decision decision = new SimpleDecision( workflow, decisionKey, approverPrincipal ); + final Decision decision = new SimpleDecision( workflow.getId(), workflow.getAttributes(), decisionKey, approverPrincipal ); // Add facts to the Decision, if any were supplied if( facts != null ) { @@ -132,7 +132,7 @@ public final class WorkflowBuilder { // If rejected, sent a notification if ( rejectedMessageKey != null ) { - final SimpleNotification rejectNotification = new SimpleNotification( workflow, rejectedMessageKey, submitter ); + final SimpleNotification rejectNotification = new SimpleNotification( workflow.getId(), workflow.getAttributes(), rejectedMessageKey, submitter ); decision.addSuccessor( Outcome.DECISION_DENY, rejectNotification ); } @@ -158,9 +158,9 @@ public final class WorkflowBuilder { // Make sure our tasks have this workflow as the parent, then return if( prepTask != null ) { - prepTask.setWorkflow( workflow ); + prepTask.setWorkflow( workflow.getId(), workflow.getAttributes() ); } - completionTask.setWorkflow( workflow ); + completionTask.setWorkflow( workflow.getId(), workflow.getAttributes() ); return workflow; } diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowEventEmitter.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowEventEmitter.java index 8c2f899..8ecabd5 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowEventEmitter.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/WorkflowEventEmitter.java @@ -18,9 +18,12 @@ */ package org.apache.wiki.workflow; +import org.apache.wiki.event.WikiEventListener; import org.apache.wiki.event.WikiEventManager; import org.apache.wiki.event.WorkflowEvent; +import java.util.Set; + /** * Emits all kind of {@link WorkflowEvent}s. @@ -39,4 +42,14 @@ public enum WorkflowEventEmitter { } } + public static void registerListener( final WikiEventListener listener ) { + if ( WikiEventManager.isListening( get() ) ) { + final Set< WikiEventListener > attachedListeners = WikiEventManager.getWikiEventListeners( get() ); + attachedListeners.stream() + .filter( l -> listener.getClass().isAssignableFrom( l.getClass() ) ) + .forEach( WikiEventManager::removeWikiEventListener ); + } + WikiEventManager.addWikiEventListener( WorkflowEventEmitter.get(), listener ); + } + } diff --git a/jspwiki-main/src/test/java/org/apache/wiki/auth/UserManagerTest.java b/jspwiki-main/src/test/java/org/apache/wiki/auth/UserManagerTest.java index 7565109..aecf7de 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/auth/UserManagerTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/auth/UserManagerTest.java @@ -326,7 +326,7 @@ public class UserManagerTest { Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_LOGIN_NAME, profile.getLoginName() ), facts.get( 1 ) ); Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_SUBMITTER, session.getUserPrincipal().getName() ), facts.get( 2 ) ); Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_EMAIL, profile.getEmail() ), facts.get( 3 ) ); - Assertions.assertEquals( profile, d.getWorkflow().getAttribute( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ) ); + Assertions.assertEquals( profile, d.getWorkflowContext().get( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ) ); // Approve the profile d.decide( Outcome.DECISION_APPROVE ); @@ -377,7 +377,7 @@ public class UserManagerTest { Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_LOGIN_NAME, profile.getLoginName() ), facts.get( 1 ) ); Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_SUBMITTER, session.getUserPrincipal().getName() ), facts.get( 2 ) ); Assertions.assertEquals( new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_EMAIL, profile.getEmail() ), facts.get( 3 ) ); - Assertions.assertEquals( profile, d.getWorkflow().getAttribute( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ) ); + Assertions.assertEquals( profile, d.getWorkflowContext().get( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE ) ); // Approve the profile d.decide( Outcome.DECISION_DENY ); diff --git a/jspwiki-main/src/test/java/org/apache/wiki/workflow/ApprovalWorkflowTest.java b/jspwiki-main/src/test/java/org/apache/wiki/workflow/ApprovalWorkflowTest.java index 7f25b9e..ba05915 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/workflow/ApprovalWorkflowTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/workflow/ApprovalWorkflowTest.java @@ -28,36 +28,22 @@ import org.apache.wiki.auth.WikiPrincipal; import org.apache.wiki.filters.FilterManager; import org.apache.wiki.pages.PageManager; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.security.Principal; import java.util.Collection; import java.util.List; -import java.util.Properties; -public class ApprovalWorkflowTest { - - WorkflowBuilder m_builder; - TestEngine m_engine; - WorkflowManager m_wm; - DecisionQueue m_dq; - - @BeforeEach - public void setUp() throws Exception { - final Properties props = TestEngine.getTestProperties(); +import static org.apache.wiki.TestEngine.with; - // Explicitly turn on Admin approvals for page saves and our sample approval workflow - props.put("jspwiki.approver.workflow.saveWikiPage", "Admin"); - props.put( "jspwiki.approver.workflow.approvalWorkflow", Users.JANNE ); - // Start the wiki engine - m_engine = new TestEngine( props ); - m_wm = m_engine.getManager( WorkflowManager.class ); - m_dq = m_wm.getDecisionQueue(); - m_builder = WorkflowBuilder.getBuilder( m_engine ); - } +public class ApprovalWorkflowTest { + static TestEngine m_engine = TestEngine.build( with( "jspwiki.approver.workflow.saveWikiPage", "Admin" ), + with( "jspwiki.approver.workflow.approvalWorkflow", Users.JANNE ) ); + static WorkflowManager m_wm = m_engine.getManager( WorkflowManager.class ); + static DecisionQueue m_dq = m_wm.getDecisionQueue(); + static WorkflowBuilder m_builder = WorkflowBuilder.getBuilder( m_engine ); @Test public void testBuildApprovalWorkflow() throws WikiException { @@ -66,15 +52,15 @@ public class ApprovalWorkflowTest { final Task prepTask = new TestPrepTask( "task.preSaveWikiPage" ); final String decisionKey = "decision.saveWikiPage"; final Fact[] facts = new Fact[3]; - facts[0] = new Fact("fact1", 1 ); - facts[1] = new Fact("fact2","A factual String"); - facts[2] = new Fact("fact3",Outcome.DECISION_ACKNOWLEDGE); + facts[ 0 ] = new Fact( "fact1", 1 ); + facts[ 1 ] = new Fact( "fact2", "A factual String" ); + facts[ 2 ] = new Fact( "fact3", Outcome.DECISION_ACKNOWLEDGE ); final Task completionTask = new TestPrepTask( "task.saveWikiPage" ); final String rejectedMessageKey = "notification.saveWikiPage.reject"; - final Workflow w = m_builder.buildApprovalWorkflow(submitter, workflowApproverKey, - prepTask, decisionKey, facts, - completionTask, rejectedMessageKey); + final Workflow w = m_builder.buildApprovalWorkflow( submitter, workflowApproverKey, + prepTask, decisionKey, facts, + completionTask, rejectedMessageKey ); // Check to see if the workflow built correctly Assertions.assertFalse( w.isStarted() || w.isCompleted() || w.isAborted() ); @@ -137,10 +123,10 @@ public class ApprovalWorkflowTest { final String workflowApproverKey = "workflow.approvalWorkflow"; final Task prepTask = new TestPrepTask( "task.preSaveWikiPage" ); final String decisionKey = "decision.saveWikiPage"; - final Fact[] facts = new Fact[3]; - facts[0] = new Fact("fact1", 1 ); - facts[1] = new Fact("fact2","A factual String"); - facts[2] = new Fact("fact3",Outcome.DECISION_ACKNOWLEDGE); + final Fact[] facts = new Fact[ 3 ]; + facts[ 0 ] = new Fact( "fact1", 1 ); + facts[ 1 ] = new Fact( "fact2", "A factual String" ); + facts[ 2 ] = new Fact( "fact3", Outcome.DECISION_ACKNOWLEDGE ); final Task completionTask = new TestPrepTask( "task.saveWikiPage" ); final String rejectedMessageKey = "notification.saveWikiPage.reject"; @@ -177,25 +163,23 @@ public class ApprovalWorkflowTest { // Create a sample test page and try to save it final String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); final String text = "This is a test!"; - try { - m_engine.saveTextAsJanne( pageName, text ); - } catch( final DecisionRequiredException e ) { - // Swallow exception, because it is expected... - } + + // Expected exception... + Assertions.assertThrows( DecisionRequiredException.class, () -> m_engine.saveTextAsJanne( pageName, text ) ); // How do we know the workflow works? Well, first of all the page shouldn't exist yet... - Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists(pageName)); + Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists( pageName ) ); // Second, GroupPrincipal Admin should see a Decision in its queue Collection< Decision > decisions = m_dq.getActorDecisions( m_engine.adminSession() ); - Assertions.assertEquals(1, decisions.size()); + Assertions.assertEquals( 1, decisions.size() ); // Now, approve the decision and it should go away, and page should appear. final Decision decision = decisions.iterator().next(); - decision.decide(Outcome.DECISION_APPROVE); - Assertions.assertTrue( m_engine.getManager( PageManager.class ).wikiPageExists(pageName)); + decision.decide( Outcome.DECISION_APPROVE ); + Assertions.assertTrue( m_engine.getManager( PageManager.class ).wikiPageExists( pageName ) ); decisions = m_dq.getActorDecisions( m_engine.adminSession() ); - Assertions.assertEquals(0, decisions.size()); + Assertions.assertEquals( 0, decisions.size() ); // Delete the page we created m_engine.getManager( PageManager.class ).deletePage( pageName ); @@ -213,27 +197,27 @@ public class ApprovalWorkflowTest { } // How do we know the workflow works? Well, first of all the page shouldn't exist yet... - Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists(pageName)); + Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists( pageName ) ); // ...and there should be a Decision in GroupPrincipal Admin's queue Collection< Decision > decisions = m_dq.getActorDecisions( m_engine.adminSession() ); - Assertions.assertEquals(1, decisions.size()); + Assertions.assertEquals( 1, decisions.size() ); // Now, DENY the decision and the page should still not exist... Decision decision = decisions.iterator().next(); - decision.decide(Outcome.DECISION_DENY); - Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists(pageName) ); + decision.decide( Outcome.DECISION_DENY ); + Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists( pageName ) ); // ...but there should also be a notification decision in Janne's queue decisions = m_dq.getActorDecisions( m_engine.janneSession() ); - Assertions.assertEquals(1, decisions.size()); + Assertions.assertEquals( 1, decisions.size() ); decision = decisions.iterator().next(); - Assertions.assertEquals(WorkflowManager.WF_WP_SAVE_REJECT_MESSAGE_KEY, decision.getMessageKey()); + Assertions.assertEquals( WorkflowManager.WF_WP_SAVE_REJECT_MESSAGE_KEY, decision.getMessageKey() ); // Once Janne disposes of the notification, his queue should be empty - decision.decide(Outcome.DECISION_ACKNOWLEDGE); + decision.decide( Outcome.DECISION_ACKNOWLEDGE ); decisions = m_dq.getActorDecisions( m_engine.janneSession() ); - Assertions.assertEquals(0, decisions.size()); + Assertions.assertEquals( 0, decisions.size() ); } @Test @@ -245,12 +229,9 @@ public class ApprovalWorkflowTest { // Create a sample test page and try to save it final String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); final String text = "This is a test!"; - try - { + try { m_engine.saveTextAsJanne(pageName, text); - } - catch ( final WikiException e ) - { + } catch( final WikiException e ) { Assertions.assertTrue( e instanceof FilterException ); Assertions.assertEquals( "Page save aborted.", e.getMessage() ); return; @@ -262,8 +243,8 @@ public class ApprovalWorkflowTest { * Sample "prep task" that sets an attribute in the workflow indicating * that it ran successfully, */ - public static class TestPrepTask extends Task - { + public static class TestPrepTask extends Task { + private static final long serialVersionUID = 1L; public TestPrepTask( final String messageKey ) @@ -273,7 +254,7 @@ public class ApprovalWorkflowTest { @Override public Outcome execute() { - getWorkflow().setAttribute( getMessageKey(), "Completed" ); + getWorkflowContext().put( getMessageKey(), "Completed" ); setOutcome( Outcome.STEP_COMPLETE ); return Outcome.STEP_COMPLETE; } diff --git a/jspwiki-main/src/test/java/org/apache/wiki/workflow/DecisionQueueTest.java b/jspwiki-main/src/test/java/org/apache/wiki/workflow/DecisionQueueTest.java index 9ac1201..fdcf1f9 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/workflow/DecisionQueueTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/workflow/DecisionQueueTest.java @@ -54,12 +54,12 @@ public class DecisionQueueTest { adminSession = m_engine.adminSession(); janneSession = m_engine.janneSession(); w = new Workflow("workflow.key", new WikiPrincipal("Owner1")); - d1 = new SimpleDecision(w, "decision1.key", new GroupPrincipal("Admin")); - d2 = new SimpleDecision(w, "decision2.key", new WikiPrincipal("Owner2")); - d3 = new SimpleDecision(w, "decision3.key", janneSession.getUserPrincipal()); - m_queue.add(d1); - m_queue.add(d2); - m_queue.add(d3); + d1 = new SimpleDecision( w.getId(), w.getAttributes(), "decision1.key", new GroupPrincipal( "Admin" ) ); + d2 = new SimpleDecision( w.getId(), w.getAttributes(), "decision2.key", new WikiPrincipal( "Owner2" ) ); + d3 = new SimpleDecision( w.getId(), w.getAttributes(), "decision3.key", janneSession.getUserPrincipal() ); + m_queue.add( d1 ); + m_queue.add( d2 ); + m_queue.add( d3 ); } @Test @@ -150,38 +150,37 @@ public class DecisionQueueTest { } @Test - public void testDecisionWorkflow() throws WikiException - { + public void testDecisionWorkflow() throws WikiException { final Principal janne = janneSession.getUserPrincipal(); // Clean out the queue first - m_queue.remove(d1); - m_queue.remove(d2); - m_queue.remove(d3); + m_queue.remove( d1 ); + m_queue.remove( d2 ); + m_queue.remove( d3 ); // Create a workflow with 3 steps, with a Decision for Janne in the middle - w = new Workflow("workflow.key", new WikiPrincipal("Owner1")); - final Step startTask = new TaskTest.NormalTask(w); - final Step endTask = new TaskTest.NormalTask(w); - final Decision decision = new SimpleDecision(w, "decision.Actor1Decision", janne); - startTask.addSuccessor(Outcome.STEP_COMPLETE, decision); - decision.addSuccessor(Outcome.DECISION_APPROVE, endTask); - w.setFirstStep(startTask); + w = new Workflow( "workflow.key", new WikiPrincipal( "Owner1" ) ); + final Step startTask = new TaskTest.NormalTask( w ); + final Step endTask = new TaskTest.NormalTask( w ); + final Decision decision = new SimpleDecision( w.getId(), w.getAttributes(), "decision.Actor1Decision", janne ); + startTask.addSuccessor( Outcome.STEP_COMPLETE, decision ); + decision.addSuccessor( Outcome.DECISION_APPROVE, endTask ); + w.setFirstStep( startTask ); // Start the workflow, and verify that the Decision is the current Step w.start(); - Assertions.assertEquals(decision, w.getCurrentStep()); + Assertions.assertEquals( decision, w.getCurrentStep() ); // Verify that it's also in Janne's DecisionQueue - Collection< Decision > decisions = m_queue.getActorDecisions(janneSession); - Assertions.assertEquals(1, decisions.size()); - final Decision d = (Decision)decisions.iterator().next(); - Assertions.assertEquals(decision, d); + Collection< Decision > decisions = m_queue.getActorDecisions( janneSession ); + Assertions.assertEquals( 1, decisions.size() ); + final Decision d = ( Decision )decisions.iterator().next(); + Assertions.assertEquals( decision, d ); // Make Decision, and verify that it's gone from the queue - m_queue.decide(decision, Outcome.DECISION_APPROVE); - decisions = m_queue.getActorDecisions(janneSession); - Assertions.assertEquals(0, decisions.size()); + m_queue.decide( decision, Outcome.DECISION_APPROVE ); + decisions = m_queue.getActorDecisions( janneSession ); + Assertions.assertEquals( 0, decisions.size() ); } } diff --git a/jspwiki-main/src/test/java/org/apache/wiki/workflow/SimpleDecisionTest.java b/jspwiki-main/src/test/java/org/apache/wiki/workflow/SimpleDecisionTest.java index 5bf3539..6cd4e86 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/workflow/SimpleDecisionTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/workflow/SimpleDecisionTest.java @@ -36,7 +36,7 @@ public class SimpleDecisionTest { @BeforeEach public void setUp() throws Exception { m_workflow = new Workflow( "workflow.key", new WikiPrincipal( "Owner1" ) ); - m_decision = new SimpleDecision( m_workflow, "decision.key", new WikiPrincipal( "Actor1" ) ); + m_decision = new SimpleDecision( m_workflow.getId(), m_workflow.getAttributes(), "decision.key", new WikiPrincipal( "Actor1" ) ); } @Test @@ -79,11 +79,11 @@ public class SimpleDecisionTest { @Test public void testSuccessors() { // If the decision is approved, branch to another decision (d2) - final Step d2 = new SimpleDecision( m_workflow, "decision2.key", new WikiPrincipal( "Actor1" ) ); + final Step d2 = new SimpleDecision( m_workflow.getId(), m_workflow.getAttributes(), "decision2.key", new WikiPrincipal( "Actor1" ) ); m_decision.addSuccessor( Outcome.DECISION_APPROVE, d2 ); // If the decision is denied, branch to another decision (d3) - final Step d3 = new SimpleDecision( m_workflow, "decision3.key", new WikiPrincipal( "Actor1" ) ); + final Step d3 = new SimpleDecision( m_workflow.getId(), m_workflow.getAttributes(), "decision3.key", new WikiPrincipal( "Actor1" ) ); m_decision.addSuccessor( Outcome.DECISION_DENY, d3 ); Assertions.assertEquals( d2, m_decision.getSuccessor( Outcome.DECISION_APPROVE ) ); @@ -148,7 +148,7 @@ public class SimpleDecisionTest { @Test public void testGetWorkflow() { - Assertions.assertEquals( m_workflow, m_decision.getWorkflow() ); + Assertions.assertEquals( m_workflow.getId(), m_decision.getWorkflowId() ); } @Test diff --git a/jspwiki-main/src/test/java/org/apache/wiki/workflow/TaskTest.java b/jspwiki-main/src/test/java/org/apache/wiki/workflow/TaskTest.java index 6e330c0..53612e9 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/workflow/TaskTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/workflow/TaskTest.java @@ -40,7 +40,7 @@ public class TaskTest public NormalTask( final Workflow workflow) { - super(workflow, "task.normal"); + super(workflow.getId(), workflow.getAttributes(), "task.normal"); } public Outcome execute() throws WikiException { @@ -56,7 +56,7 @@ public class TaskTest public ErrorTask( final Workflow workflow) { - super(workflow, "task.error"); + super(workflow.getId(), workflow.getAttributes(), "task.error"); } public Outcome execute() throws WikiException { @@ -86,11 +86,11 @@ public class TaskTest public void testSuccessors() { // If task finishes normally, branch to a decision (d1) - final Step d1 = new SimpleDecision(m_workflow, "decision1.key", new WikiPrincipal("Actor1")); + final Step d1 = new SimpleDecision(m_workflow.getId(), m_workflow.getAttributes(), "decision1.key", new WikiPrincipal("Actor1")); m_task.addSuccessor(Outcome.STEP_COMPLETE, d1); // If the task aborts, branch to an alternate decision (d2) - final Step d2 = new SimpleDecision(m_workflow, "decision2.key", new WikiPrincipal("Actor2")); + final Step d2 = new SimpleDecision(m_workflow.getId(), m_workflow.getAttributes(), "decision2.key", new WikiPrincipal("Actor2")); m_task.addSuccessor(Outcome.STEP_ABORT, d2); Assertions.assertEquals(d1, m_task.getSuccessor(Outcome.STEP_COMPLETE)); @@ -171,7 +171,7 @@ public class TaskTest @Test public void testGetWorkflow() { - Assertions.assertEquals(m_workflow, m_task.getWorkflow()); + Assertions.assertEquals(m_workflow.getId(), m_task.getWorkflowId() ); } @Test diff --git a/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java b/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java index 2597ca9..8975859 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java @@ -41,7 +41,7 @@ public class WorkflowManagerTest { w = new Workflow( "workflow.key", new WikiPrincipal( "Owner1" ) ); final Step startTask = new TaskTest.NormalTask( w ); final Step endTask = new TaskTest.NormalTask( w ); - final Decision decision = new SimpleDecision( w, "decision.editWikiApproval", new WikiPrincipal( "Actor1" ) ); + final Decision decision = new SimpleDecision( w.getId(), w.getAttributes(), "decision.editWikiApproval", new WikiPrincipal( "Actor1" ) ); startTask.addSuccessor( Outcome.STEP_COMPLETE, decision ); decision.addSuccessor( Outcome.DECISION_APPROVE, endTask ); w.setFirstStep( startTask ); @@ -52,9 +52,7 @@ public class WorkflowManagerTest { @Test public void testStart() throws WikiException { - // Once we start the workflow, it should show that it's started - // and the WM should have assigned it an ID - Assertions.assertEquals( Workflow.ID_NOT_SET, w.getId() ); + // Once we start the workflow, it should show that it's started and the WM should have assigned it an ID Assertions.assertFalse( w.isStarted() ); wm.start( w ); Assertions.assertNotEquals( Workflow.ID_NOT_SET, w.getId() ); @@ -63,17 +61,16 @@ public class WorkflowManagerTest { @Test public void testWorkflows() throws WikiException { - // There should be no workflows in the cache, and none in completed list - Assertions.assertEquals( 0, wm.getWorkflows().size() ); + // After Workflow being created, it gets added to the workflows cache, there should be none in the completed list + Assertions.assertEquals( 1, wm.getWorkflows().size() ); Assertions.assertEquals( 0, wm.getCompletedWorkflows().size() ); - // After starting, there should be 1 in the cache, with ID=1 + // After starting, there should be 1 in the cache wm.start( w ); Assertions.assertEquals( 1, wm.getWorkflows().size() ); Assertions.assertEquals( 0, wm.getCompletedWorkflows().size() ); final Workflow workflow = wm.getWorkflows().iterator().next(); Assertions.assertEquals( w, workflow ); - Assertions.assertEquals( 1, workflow.getId() ); // After forcing a decision on step 2, the workflow should complete and vanish from the cache final Decision d = ( Decision )w.getCurrentStep(); diff --git a/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowTest.java b/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowTest.java index 84b7444..6064c28 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowTest.java @@ -32,6 +32,8 @@ import java.util.Date; public class WorkflowTest { + WorkflowManager workflowsEventListener; + Workflow w; Task initTask; @@ -44,6 +46,9 @@ public class WorkflowTest { @BeforeEach public void setUp() throws Exception { + // listen to workflow events + workflowsEventListener = new DefaultWorkflowManager(); + // Create workflow; owner is test user w = new Workflow( "workflow.myworkflow", new WikiPrincipal( "Owner1" ) ); @@ -55,7 +60,7 @@ public class WorkflowTest { // Create an intermetidate decision step final Principal actor = new GroupPrincipal( "Admin" ); - decision = new SimpleDecision( w, "decision.AdminDecision", actor ); + decision = new SimpleDecision( w.getId(), w.getAttributes(), "decision.AdminDecision", actor ); // Hook the steps together initTask.addSuccessor( Outcome.STEP_COMPLETE, decision ); @@ -74,7 +79,6 @@ public class WorkflowTest { Assertions.assertNull( w.getCurrentStep() ); Assertions.assertNull( w.getCurrentActor() ); Assertions.assertEquals( 0, w.getHistory().size() ); - Assertions.assertEquals( Workflow.ID_NOT_SET, w.getId() ); Assertions.assertEquals( new WikiPrincipal( "Owner1" ), w.getOwner() ); Assertions.assertEquals( Workflow.CREATED, w.getCurrentState() ); Assertions.assertEquals( Step.TIME_NOT_SET, w.getStartTime() ); @@ -355,7 +359,6 @@ public class WorkflowTest { @Test public void testSetId() { - Assertions.assertEquals( Workflow.ID_NOT_SET, w.getId() ); w.setId( 1001 ); Assertions.assertEquals( 1001, w.getId() ); }
