»
S
I
D
E
B
A
R
«
How to skip a step in a Spring AbstractWizardFormController
Jul 22nd, 2009 by franciov

spring-framework-2.5

Here we are again dealing with the Abstract Wizard Form Controller, a form controller for typical wizard-style workflows, provided by Spring, a Java Web Application Framework. This time we discuss a more frequent problem: skipping steps.

For istance, let’s consider the situation in which you are at page n of your wizard and submitting a form for target n+1. What if, for some reason, you need to skip the step n+1 and go forward one step?

The first attempt to face this problem might consists in conditionally changing the target value inside the form included in the jsp page corresponding to the view for page n. For example we might switch between passing the request parameter _target3 or _target4 by using the JSTL Core TagLib. This works, but we are putting a conditional control inside a View, that is a very bad practice in a MVC-based framework like Spring.

Let’s move the conditional control into our wizard controller, and to be more precise into the getTargetPage method:

protected int getTargetPage(HttpServletRequest request, Object command, Errors errors, int currentPage) {

        if (currentPage == 2 /* n */) {
                if (/* insert your condition here*/) {
                        return 3; /* n+1 */
                }
        }

        return super.getTargetPage(request, command, errors, currentPage);
    }

This solution is clean and makes your application more readable, your code more reusable and so on…

But what if the step we want to jump to is the final step? We cannot reach it by using the getTargetPage method because the final step has no page number assigned: the finish action is triggered by the request parameter _finish and the view name is retrieved by the ModelAndView object that the processFinish method returns.

To solve this problem we can modify the view name for target n+1 at runtime by overriding the getViewName method as following:

protected String getViewName(HttpServletRequest request, Object command, int page) {

        if (page == 3 /* n+1 */) {
                retun "redirect:myWizard.html?_finish=";  // go to final step
        }

        return super.getViewName(request, command, page);
    }

In this way we simply redirect the flow to the final step. Obviously, at finish all pages get validated to guarantee a consistent state, so you cannot skip a step whose page needs validation.

Spring AbstractWizardFormController, how to manipulate pages at runtime
Jul 19th, 2009 by franciov

spring-framework-2.5
The Abstract Wizard Form Controller is a form controller for typical wizard-style workflows, provided by Spring, a Java Web Application Framework.

In contrast to classic forms, wizards have more than one form view page. Therefore, there are various actions instead of one single submit action:

  • finish: trying to leave the wizard successfully, i.e. performing its final action, and thus needing a valid state;
  • cancel: leaving the wizard without performing its final action, and thus without regard to the validity of its current state;
  • page change: showing another wizard page, e.g. the next or previous one, with regard to “dirty back” and “dirty forward”.

In this article I want to focus on pages, how to set them up, expecially in the uncommon situation in which you don’t know exactly the number of pages you want to put in your wizard before running the application.

Every Spring manual would suggest you to put WizardFormController’s pages in the Spring XML configuration file, and actually this is the best way to set up your pages. Let’s consider the following configuration:

<bean name="myWizardForm"
          class="forms.MyWizardForm">

    </bean>

    <bean name="/myWizard.html"
          class="control.MyWizardFormController"
          p:sessionForm="true"
          p:commandClass="forms.MyWizardForm"
          p:commandName="myWizardForm">

        <property name="pages">
            <list>
                <value>page0</value>
                <value>page1</value>
                <value>page2</value>
            </list>
        </property>
    </bean>

We have put a property named pages inside myWizard.html bean, in which we listed the page values. Each value is used as the name of the view inside your jsp folder.

And now the wizard form, you can set it up for your needs.

public class MyWizardForm {

    // put your fields here

    public MyWizardForm() {
        // initialization
    }

    // put your setters and getters here
}

Finally the wizard controller.

public class MyWizardFormController extends AbstractWizardFormController {

    public MyWizardFormController() {
    }

    protected Map referenceData(HttpServletRequest request, Object command, Errors errors, int page) throws Exception {

        Map map = new HashMap();
        // put information you need into the map

        return map;
    }

    protected ModelAndView processFinish(HttpServletRequest request, HttpServletResponse response, Object object, BindException exception) throws Exception {
        return new ModelAndView("finish");
    }

What if, for some reasons, you need to create or edit your wizard page list at runtime? Let’s face the problem.

First of all, you can override the XML configuration through the setPages method. The following example replaces the page list specified in the XML file with an identical one but that has been built at run-time in MyWizardFormController constructor.

public MyWizardFormController() {
        String pages[] = new String[3];
        pages[0] = "page0";
        pages[1] = "page1";
        pages[2] = "page2";
        this.setPages(pages);
    }
}

This doesn’t help much: we moved the configuration outside XML (that’s a bad thing) but we still have a fixed number of pages with fixed values.

Let’s consider an application in which the page values are the request parameter keys for the first step (i.e. _target0). We might call setPages inside an overridden method used before the wizard controller accesses to page list, such as the getInitialPage method.

public class MyWizardFormController extends AbstractWizardFormController {

    public MyWizardFormController() {
    }

    protected int getInitialPage(HttpServletRequest request, Object command) {

        // override pages only when the target page is 0
        if (this.getTargetPage(request, 0) == 0) {

            // retrieve keys from request parameters
            Set parameters = new HashSet(request.getParameterMap().keySet());
            Iterator parametersIterator = parameters.iterator();
            String pages[] = new String[parameters.size()1];
            int i = 0;

            // fill wizard pages
            while (parametersIterator.hasNext()) {
                String page = (String) parametersIterator.next();
                // let’s ignore the ‘_target0′ parameter key
                if (page.startsWith("_") == false){
                    pages[i] = page;
                    i++;
                }
            }

            this.setPages(pages);  
        }

        return super.getInitialPage(request, command);
    }

In this way, as soon as myWizard.html bean is invoked by clicking on a link (i.e. GET http request) or submitting a form (i.e. POST http request), the page list is dinamically built starting from request parameter keys. This would be more useful if you decide to use request parameters values, or attributes.

For my purposes I used request parameter keys retrieved from a form submission in order to run an unordered set of pages and reporting each result (i.e. validation) in the final step.

»  Substance: WordPress   »  Style: Ahren Ahimsa
© www.francesco.iovine.name