|
There is a very trivial demo application included in the source distribution of
the Struts Workflow Extension that demonstrates how a wizard-like sequence of
dialogs can be implemented. This wizard demo application consists of three screens:
The first two screens prompt the user for the first and the second
operand. The third screen presents the result, which is just the sum of the two
operands the user entered. This is about the simplest wizard-like sequence of dialogs
you can think of. Yet it is well-suited to demonstrate, how a wizard can be made
bulletproof with the Struts Workflow Extension. For this, three issues need
to be tackled: the user must not be able to enter the wizard in the middle, he must
not be able to leave it uncontrolled and re-submitting a form must be handled
gracefully.
You can live-access
the demo application here.
However, it makes much more sense to download the demo
application and install it locally in your JSP container, so you can look at the log output,
when progressing through it. This is essential to understand what's going on.
This is what the demo application looks like, when you access the index page:
When the link "Start Wizard" is clicked, the first screen of the wizard is displayed:
In this dialog the user is prompted for a valid integer number. If the user decides to
click on one of the links "Home" or "Start Wizard" in the side navigation (which is left
active for demonstration purposes here, so you can easily simulate unexpected requests),
the wizard workflow is violated and the user is brought back to the same screen which
then also displays an error message:
The only valid requests are issued, if the user clicks the "Cancel" or "Next" button, the
first one terminating the wizard in a well-defined way, the second one allowing the user
to progress to the next screen:
In the second screen the user is also trapped, until he uses one of the buttons "Back",
"Next" or "Cancel". Hitting the browser's "Reload"-Button which would normally cause
a double submit, is also handled gracefully, i. e. a workflow violation is detected
and the user is brought back to the same screen.
After the user has finally inserted the second operand, he is forwarded to the third
screen, which presents the sum of the two operands as the result.
If the user tries to enter the wizard right in the middle (e. g. by typing in the url
"displayWizardScreen2.do") he is brought to the first screen.
The behaviour we are seeing here, that the user is captured in a dialog, is well
known from desktop applications that can make use of modal dialogs. In web applications
you normally do not have modal dialogs available. Still, this example shows, that it is
easily possible to implement them with the Struts Workflow Extension. Having modal
dialogs available can make your life very easy, because you can be sure that your
action code is only executed after the expected preconditions are true. This relieves
you from putting code in your actions that checks these preconditions and it ensures
that your sessions stay lean and clean, because you can force the user to progress in a
dialog or end it with only a few well-defined actions. This gives you the chance to clean
up the session from the data that has been put there, when the dialog was started.
Let's walk through an example: Below is the log output that says what is going
on, when the user tries to enter the dialog right in the middle by submitting the second
screen without having walked through the first screen before:
Processing a 'POST' for path '/submitWizardScreen2'
No workflows defined
Previous primary workflow: none
Violated prevState condition of workflow: wizardScreen2. Cleaning up and removing this workflow.
Forwarding to "workflowViolation_wizardScreen2": ForwardConfig[name=workflowViolation_wizardScreen2,
path=/wizardScreen2Violation.do,redirect=false,contextRelative=false]
Processing a 'POST' for path '/wizardScreen2Violation'
No workflows defined
Previous primary workflow: none
Processing a 'POST' for path '/displayWizardScreen2'
No workflows defined
Previous primary workflow: none
Violated prevState condition of workflow: wizardScreen1. Cleaning up and removing this workflow.
Forwarding to "workflowViolation_wizardScreen1": ForwardConfig[name=workflowViolation_wizardScreen1,
path=/wizardScreen1Violation.do,redirect=false,contextRelative=false]
Processing a 'POST' for path '/wizardScreen1Violation'
No workflows defined
Previous primary workflow: none
Processing a 'POST' for path '/displayWizardScreen1'
No workflows defined
Previous primary workflow: none
Violated prevState condition of workflow: prepareWizard. Cleaning up and removing this workflow.
Forwarding to "workflowViolation_prepareWizard": ForwardConfig[name=workflowViolation_prepareWizard,
path=/startWizard.do,redirect=false,contextRelative=false]
Processing a 'POST' for path '/startWizard'
No workflows defined
Previous primary workflow: none
Updating workflow: prepareWizard
Setting currentState to initialized
Processing a 'POST' for path '/displayWizardScreen1'
Defined Workflows:
prepareWizard - Current state: initialized, Defined next states: [], Cleanup action names:
[AddSessionAttributeCleanup_wizardForm]
Previous primary workflow: prepareWizard
Updating workflow: wizardScreen1
Setting currentState to displayed
Setting definedNextStates to [submitted]
The log basically says that the prevState condition of the workflow "wizardScreen2" is violated and
therfore forwards to the action "/wizardScreen2Violation", which saves an error message and
immediately forwards to "/displayWizardScreen2". This action expects workflow "wizardScreen1"
to be in the state "submitted". As this is not true (because the user tried to submit the second
screen without having finished the first screen before), a prevState violation of "wizardScreen1" is
detected now, which causes a forward to "wizardScreen1Violation" that passes control to
"/displayWizardScreen1". This action in turn expects workflow
"prepareWizard" to be in the state "initialized", which is not true either, so it forwards to the
action "/startWizard". This action does not depend on any other workflows, i. e. it does not define
any prevState conditions. Thus, this action can be executed, brings the workflow "prepareWizard"
into the state "initialized" and forwards to "/displayWizardScreen1". This time the action can be
executed successfully, because now the prevState conditions are true.
We see, that finally the request for submitting the second screen results in the same response,
as if the user would have started the wizard normally with the action "/startWizard": The first
screen is presented to the user.
|