Chariot Training Classes

Training Courses

I run Chariot's training and mentoring services. We provide training in AngularJS, HTML5, Spring, Hibernate, Maven, Scala, and more.

Chariot Education Services

Technology

Chariot Emerging Tech

Learn about upcoming technologies and trends from my colleagues at Chariot Solutions.

Resources

Chariot Conferences

Podcasts

« Quizzo Friday - Goin' all layered on the darn thing | Main | Monday/Tuesday Quizzo - It's a mess w/a silver lining? »
Thursday
Feb232012

Wednesday / Thursday Quizzo - flowin' it

Ok, most of my time in the last two days was non-quizzo related. Well, I should say non-business logic quizzo related. See, I was chasing my tail around the integration tests for the state of the quiz, which I've set up with a facade Spring Bean. Here's my interface:

public interface QuizRunStateMachine {
  void startQuiz(Long quizId, String text);
  boolean nextQuestion();
  Map<String, BigDecimal> getScores();
  Long getCurrentQuestionId();
  boolean submitAnswer(Team team, Answer answer);
  void endQuiz();
  QuizRun getQuizRun();
}

When I test the implementation of this state machine, which flips various bits in my database and keeps track of the current state, it works fine within IntelliJ (my IDE weapon of choice). However, when I use the maven test process, all tests fail due to the assertions coded within the bean. Grr.

I have no answer for that...

See above.

Anyhoo... Web flow, anyone?

Roo makes setting up the web flow engine easy. You just issue this simple command:

roo> web flow setup --flowName playQuizzo

The command sets up Web Flow, putting the flow itself in the WEB-INF/views/playQuizzo directory.

This blog entry lays out the beginning of using this strategy to implement a quiz player interface using web flow. It's incoherent a bit, as I'm sort of live-blogging this, so at some point I may go back and clean it up. But for now, hopefully it will serve as a helpful guide.

The flow

Here is a simple web flow (so far) that I've put together to handle the quiz flow:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/webflow     
   http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">

  <var class="com.chariot.games.quizzo.web.flow.TeamSetupForm" 
        name="teamSetupForm"/>

  <view-state id="register-team" 
            view="playQuizzo/register-team"
           model="flowScope.teamSetupForm">
    <transition on="continue" to="register-team-members" 
       bind="true" validate="true">
      <evaluate expression="quizzoFlowManager.debug(flowRequestContext)"/>
    </transition>
  </view-state>

  <view-state id="register-team-members" 
            view="playQuizzo/register-team-members"
           model="flowScope.teamSetupForm">
    <transition on="continue" to="ready-to-play"/>
    <transition on="add-team-member"/>
    <transition on="remove-team-member"/>
    <transition on="back" to="register-team"/>
  </view-state>

  <view-state id="ready-to-play" view="playQuizzo/ready-to-play">   
    <transition on="go" to="play-round">
      
    </transition>
  </view-state>

  <view-state id="play-round" view="playQuizzo/play-round">    
    <transition on="submit-answer" to="play-round"/>
    <transition on="finish" to="game-over"/>
  </view-state>

  <end-state id="game-over" view="playQuizzo/game-over"/>

</flow>

This web flow is relatively simple- it requires the users to register their teams and team members, then keeps presenting the current question from the quiz run state machine, and as the submissions progress, the users keep submitting answers for each question, etc...

I haven't worked all of this out yet, and as I'm writing this blog post I'm about to commit at least a lame beginning to the scripts, but you get the idea.

Building our form views

Ok, so I have a love-hate relationship with the web tier of Roo. For simple cases, if you don't go off the rails (couldn't resist), it works fine. However, if you want to free-style it, maybe such as set up non-RESTful views such as web flows, it breaks down because of the implicit conventions (specifically the form has to be RESTful in some way, includes a primary key, optional version, and most importantly, those dang Submit buttons.

I'm using Web Flow for the quiz takers. It makes sense to me (so far) that the quiz manager will control the flow using a specific set of protected pages only he can get to, which interact with the manager methods of my state machine (starting a quiz, advancing to the next question, etc).

However, the users will be walking through a directed web flow. The flow will have various actions, and they will be whatever I want them to be - bottom line, I have to break with Roo conventions. So I'm setting up my own Roo tags for a generic form, button grouping, and buttons.

The Form

I will update this as I go along, but here is my current generic "form" tag, which I place alongside form:create and others as form:form:

<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core" 
          xmlns:fn="http://java.sun.com/jsp/jstl/functions"
          xmlns:util="urn:jsptagdir:/WEB-INF/tags/util" 
          xmlns:form="http://www.springframework.org/tags/form"
          xmlns:jsp="http://java.sun.com/JSP/Page" 
          xmlns:spring="http://www.springframework.org/tags" version="2.0">

    <jsp:output omit-xml-declaration="yes"/>

    

    <c:set var="enctype" value="application/x-www-form-urlencoded"/>

    <c:if test="${multipart}">
        <c:set var="enctype" value="multipart/form-data"/>
    </c:if>

    <c:if test="${empty action}">
        <spring:url value="${path}" var="action"/>
    </c:if>

    <c:if test="${empty path}">
        <spring:url value="/playQuizzo" var="${path}"/>
    </c:if>

    <c:if test="${empty label}">
        <spring:message var="label" code="${labelCode}"/>
    </c:if>

    <util:panel id="${id}" title="${label}" openPane="${openPane}">
        <form:form method="POST" action="${path}"
                   modelAttribute="${modelAttribute}"
                   enctype="${enctype}">
            <form:errors cssClass="errors" delimiter="&lt;p/&gt;"/>
            <jsp:doBody/>
        </form:form>

    </util:panel>
</jsp:root>

I have the when and otherwise in there to handle the case where we're submitting back to ourselves (web flow) and otherwise, if a path was given, submit to that path.

Anyhoo... the button group

Ok, so I also made a buttons tag to group buttons together, because on the Roo forms the singluar button was wrapped in a styled div:

<jsp:root ...>
  <jsp:output omit-xml-declaration="yes"/>
  <div class="submit" id="form_submit">
      <jsp:doBody />
  </div>
</jsp:root>

Really just a wrapper div, you know...

The button tag

Then, I put together a button tag which configures buttons for the form. You can add as many as you want. This is in tags/form/field/button.tagx:

<jsp:root 
   xmlns:c="http://java.sun.com/jsp/jstl/core" 
   xmlns:fn="http://java.sun.com/jsp/jstl/functions"
   xmlns:spring="http://www.springframework.org/tags" 
   xmlns:form="http://www.springframework.org/tags/form"
   xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
    <jsp:output omit-xml-declaration="yes"/>

   

    <c:if test="${empty text}">
        <spring:message code="label_${fn:toLowerCase(fn:substringAfter(id,'_'))}" var="label" htmlEscape="false"/>
    </c:if>

    <c:set var="sec_field">
        <spring:escapeBody javaScriptEscape="true">${field}</spring:escapeBody>
    </c:set>

    <c:if test="${empty validate or validate}">
        
    </c:if>

    <input id="_${sec_id}_id" type="submit" value="${text}" name="${value}"/>
</jsp:root>

The gist of this tag is to create the same validator declaration on the buttons (as long as they need validation) as before. Note that I don't turn my buttons into Dojo buttons. Neither does the Roo team. I guess they felt that the standard submit buttons were OK. My goal here is to make the library conform, so any stylesheet manipulation I do will work equally well for scaffolded forms as well as my new ones.

Using the forms library, or where I got cranky

Ok, you see now my goal was to make life easy for web flow and non-scaffolded forms developers. Also I wanted to extend what we already had - not dip into the Spring MVC form tag libraries for fields, but use the Roo ones.

And that's where the fun stopped for a bit.

Label conventions

Let me lay this out, as I've tried to explain in Roo in Action but have more space here. The conventions that I am aware of for forms, fields, and other things are not well understood even by me. For exmample, if I plop a <fields:input%gt; tag down, and want to set the label, I have to remember to do this:

  • Set the label up in messages.properties
  • Prefix the message id itself in the properties file with label_
  • Use the label without the label_ prefix in the JSPX page
  • Use the labelCode attribute rather than label
  • Most cryptically: use _field_id as the id of the form fields, as opposed to the field id, as the tag will write an HTML tag with the un-underscored field name and id, and a Dojo dijit for the field with the underscored name. Otherwise, your field will not be submitted

I logged a few JIRA issues as suggestions to implement this tag set and also to make overall non-scaffolded ui more kind to the developer. We'll see how this evolves over time.

Using the tag library:

Ok, so that means I have something like this in my JSPX file:

  <form:form
          id="f_quizzo_flow_register_team"
          labelCode="webflow_playQuizzo_register_team_title"
          modelAttribute="teamSetupForm">
      <fields:input id="_name" field="name"
              labelCode="webflow_playquizzo_register_team_name_field_label"
              required="true" min="1" max="80" />
      <fields:buttons>
          <fields:button id="continue" text="Continue" 
                      value="_eventId_continue"/>
      </fields:buttons>
  </form:form>

and this in my properties file:

label_webflow_playquizzo_register_team_name_field_label=Team Name

That took me a bit of time to unwind as I was writing my form, but in the end, it made it more concise. And I forced myself to locallize. Well, almost completely.

Next up, I complete the web flow and get the state machine going.

More later...

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>