Mike Ward pushed a release candidate out for his excellent Waffle web framework.  Waffle has previously been compared to Ruby of Rails, but that's not right really.  Waffle has no scaffolding concept, no generation of pages/experience, no ActiveRecord for magic CRUD binding.

Waffle does do a magic binding from forms in web pages to methods in Java classes.  Action methods can be multi parameter methods that tightly map to the web form. It also does avoid XML, like it were a disease.  Until someone corrects me, I believe that it gives you the lowest lines of code for an action framework.

Waffle leaves you many choices for 'view' technology - JSP, Velocity, Freemarker, and many choices for persistence - iBatis and Hibernate (and more).

It also gives you two ways of 'marking up' the linkage from the web form (or AJAX invocation) to you action class method:  Via pragma and via Paranamer.  Pragma means the url itself needs to have some encoding in it and annotations must be used above the action methods, Paranamer means that from the fields available in the query string or POST, the method signature will be constructed before invocation.

My personal favorite sweet spot is Waffle in Paranamer mode, with Freemarker as view technology, iBatis for DB, with Sitemesh (of course) decorating all the pages.  I do not like the freemarker .ftl suffix. Not because there is anything wrong with it, but because DreamWeaver (as it ships by default) has a fixed set of file types that it can work with.  Thus, I have my freemarker templates as .htm files so they are dreamweaver ready.  This allows for UI professionals to get involved in the actual commits right?  Well perhaps if they're trained up in Perforce or Subversion.

When you're working on a Waffle application, you're left with precious little code to write, and consequentially unit test. Err, I mean precious little tests to write then implement.  It feels like something is wrong.  Dependencies for actions and session / application level components are chained together with constructors in a dependency injection style.  PicoContainer is (invisibly) used to do that, but Google's Guice could be a second implementation quite easily.

Waffle is not alone in this style to web app.  VRaptor is going down the same road, and Stripes is similar but has big love for @nnontaions.

So my contribution is the green man on the waffle front page. Mike chose the name waffle itself, and after much trawling for images I could purloin in a "Waffleman style" and a huge period trying to convince mike to abandon the name Waffle, I just plucked a martian from iStockPhoto.

Some examples. A calculator action.  Note that it is coincidentally a POJO:

    1 package com.thoughtworks.waffle.example.paranamer.action;
    2 
    3 public class CalculatorAction {
    4     public Number result;
    5
    6     public Number getResult() {
    7         return result;
    8     }
    9
   10     public void add(int firstNumber, int secondNumber) {
   11         result = firstNumber + secondNumber;
   12     }
   13
   14     public void subtract(long firstNumber, long secondNumber) {
   15         result = firstNumber - secondNumber;
   16     }
   17
   18     public void multiply(float firstNumber, Float secondNumber) {
   19         result = firstNumber * secondNumber;
   20     }
   21
   22 }
And the form that causes (via waffle) some method invocations:
   22 <form action="calculator.action" method="post">
   23
   24    <h3>Waffle example: Calculator</h3>
   25
   26    <div class="fieldRow">
   27        <label for="firstNumber">FirstNumber:</label>
   28        <input type="text" name="firstNumber" id="firstNumber" autocomplete="off"/>
   29        <br style="clear:both"/>
   30    </div>
   31    <div class="fieldRow">
   32        <label for="secondNumber">SecondNumber:</label>
   33        <input type="text" name="secondNumber" id="secondNumber" autocomplete="off"/>
   34        <br style="clear:both"/>
   35    </div>
   36
   37    <div class="fieldRow">
   38        <label for="result">Result:</label>
   39        <input type="text" name="result" id="result" value="${action.result}" disabled="disabled" readonly="readonly"/>
   40        <br style="clear:both"/>
   41    </div>
   42
   43    <a href="javascript:fireMethod('add|{firstNumber}|{secondNumber}');">Add</a> |
   44     <a href="javascript:fireMethod('subtract');">Subtract</a> |
   45     <a href="javascript:fireMethod('multiply');">Multiply</a>
   46 
   47 </form>

And the registrar (that needs to be implicated in a few extra lines in web.xml) :

    register("calculator", CalculatorAction.class);

Line 43 of the web page shows a single 'pragma' based linkeage, whereas 44 and 45 are Paranamer based (proving you can use both styles in you want to in the same application.  Clicking the 'Add' link for this example causes a POST invocation to the webapp with a URL of /calculator.action and having the following form fields:

  firstNumber=22
  secondNumber=11
  method=add|{firstNumber}|{secondNumber}

That is assuming that you chose 22 and 11 as the numbers in the field.  For the two other actions (Subtract and Multiply, lines 44 and 45), which use Paranamer for the method signature assembly, with /calculator.action as the URL:

  firstNumber=11
  secondNumber=22
  method=multiply

The colors hopefully help you see the linkage between page and method a little easier.



Published

March 16th, 2007
Reads:

Categories