Fork me on GitHub

Jandal
Web Framework

An H-MVC Web application framework built on J2EE and FreeMarker

Downloads

In the downloads repository at: downloads.xeolabs.com/jandal/

Source Code

Get the source code at GitHub: github.com/xeolabs/jandal

Read More

Event Flow
Form Submission
Service Layer
Service Synchronisation

A

fter pleasant experiences applying the H-MVC and State Machine architectural patterns to desktop applications, I thought: "why not combine them in a Web framework?". So in 2006 I had a crack at writing such a thing on top of J2EE. Even though I completed it, wrote some documentation and made some examples, I got busy elsewhere and never used it for anything significant.However, I've provided its documentation and source code here because they might be interesting to anyone developing their own Web framework. Incidentally, the Jandal idea has been reborn as a lean little Javascript framework called Subo, which lets you apply a similar pattern called Presentation-abstraction-contol (PAC) inside a Web page.

Briefly, a Jandal application is a hierarchy of controllers, which are state machines. Each controller has a FreeMarker template, a data model backing the template, and a set of zero or more child controllers. These are all switched on state transitions; on entry, each state activates for its controller a different template, model, and child controller set. A template can contain portlet-like expressions to embed views of sub-controllers. When a controller receives a request from the page fragment rendered from it's current template, it then either updates its model, transitions to another state, or fires a control event up or down the hierarchy. On receiving a control event, a state can perform a model update, transition or fire further control events, and so on. Also, you can plug services into your application, and manage locks on them to guard against concurrent access.

Example Application: NumberGuess

Lets dissect a simple number-guessing game made with Jandal, called NumberGuess. Number-guessing games are the "Hello, Worlds" of state machine frameworks, and our one looks like this screenshot:

Complete Sourcecode

NumberGuess State Machine Design

Before coding a Jandal application, we always start by designing an abstract hierarchical state machine for it. Below is our state machine for NumberGuess. The "playingState" State encompasses the whole game and is active for as long as the game is running. Within that are three sub-states for each stage of play. The initial sub-state is 'guessingState', in which the game waits for your guess. A "guess" event causes it to transition to 'incorrectState' or 'correctState', depending on whether the number is correct. A transition to 'incorrectState' takes with it an error message to display to the user.

NumberGuess Implementation

The diagram below shows the classes and FreeMarker templates in NumberGuess. At the top is the JandalFreeMarkerServlet that will deploy the whole thing. It is configured with a NumberGuessServiceSet, which provides a NumberGeneratorService to generate the next number to guess, and a NumberGuessApplication, which is the application itself. The class names of NumberGuessServiceSet and NumberGuessApplication will be specified to the servlet in its initialisation parameters, for it to instantiate at runtime.

Moving down the tree, there is the root FrameController, whose job is to bundle a template, frame.ftl, to render a frame around the whole application. It's got one anonymous State class that binds its child GameController, which is where all the action is. GameController has three States for each stage of game play: GuessingState, IncorrectState and CorrectState. It bundles a template to render for each of those States. The States each have an anonymous EventProcessor class to handle a view event. Note that GuesingState is where the player's guess is evaluated, so the EventProcessor for that State will be accessing the NumberGeneratorService.

Note that we can't have the Controllers as anonymous classes, because they need to have class files next to which JandalFreeMarkerServlet can find the bundled template files for their States. GuessingState is not an anonymous class because it's a big one and it's nicer to have it in it's own source file. For symmetry, CorrectState and IncorrectState are in their own source files as well.

NumberGuessApplication.java

As soon as an Application is started it calls its own onStart method to set itself up. In its implementation of onStart, our NumberGuessApplication's gives itself a root FrameController:

public class NumberGuessApplication extends Application {

    protected void onStart() throws JandalCoreException {
        setRootController(new FrameController());
    }
}

FrameController.java

As soon as an Application adds a root Controller to itself, it calls onStart on the Controller to immediately start it up. In its implementation of onStart, our FrameController creates for itself a "playingState" State, as an anonymous local class. As soon as a Controller adds an initial State to itself, it calls onEntry on it, to immediately enter it and make it active. When entered, our "playingState" State writes the name of its template file to an output, then adds a GameController to itself:

public class FrameController extends Controller {

    public FrameController() throws JandalCoreException {
        super("frameController");
    }

    public void onStart() throws JandalCoreException {

        addInitialState(new State("playing") {

            public void onEntry() throws JandalCoreException {
                setOutput("template", "frame.ftl");
                addChildController(new GameController());
            };
        });
    }
}

frame.ftl

Here's "playing" State's FreeMarker template. It has a logo, which is a JPEG bundled with FrameController. It inserts into it's output the view of the child GameController, which you'll recall is generated from the template belonging to the currently active State of GameController. Enclosing HTML page tags (IE. BODY etc) are rendered by FreeMarkerServlet.

<img src="${context.getResourceUrl("logo.jpg")}"/>
<br/>
<br/>
${context.getChildView("gameController")}

GameController.java

Recall that as soon as a State adds a Controller to itself, it calls the Controller's onStart method to immediately start it up. In its onStart method, our GameController adds its three States: GuessingState, IncorrectState and CorrectState:

public class GameController extends Controller {
    public GameController() throws JandalCoreException {
        super("gameController");
    }

    protected void onStart() throws JandalCoreException {
        addInitialState(new GuessingState());
        addState(new IncorrectState());
        addState(new CorrectState());
    }
}

GuessingState.java

In its onStart method, GuessingState starts off by outputting the name of its template file. Then it adds to itself an EventProcessor to handle "guess" view events. When it gets an event, the EventProcessor converts the event's "number" parameter from a string to an integer. If that fails it means that number is not a valid integer, so the EventProcessor makes GameController transition to the IncorrectState, parameterising the transition with an error message. When the EventProcessor has successfully converted the integer, it fetches the NumberGeneratorService and tests the number against it. If it is less than or equals to the value returned by the service, it makes GameController transition to IncorrectState, with an appropriate value for IncorrectState's "message" parameter. If the number is equal to the number returned by the service, it makes GameController transition to CorrectState.

public class GuessingState extends State {

    public GuessingState() throws JandalCoreException {
        super("guessingState");
    }

    protected void onEntry() throws JandalCoreException {

        setOutput("template", "guessing.ftl");

        addViewEventProcessor(new EventProcessor("guess") {

            public void onEvent() throws JandalCoreException {

                String numberStr = (String) getParam("number");

                int number = 0;
                try {
                    number = Integer.parseInt(numberStr);
                } catch (Exception cce) {
                    doTransition("incorrectState", new Params().add("message",
                            "Not an integer!"));
                    return; // Don't forget this
                }

                NumberGeneratorService numGenService = (NumberGeneratorService) getService(NumberGeneratorService.class
                        .getName());

                int numberToGuess = numGenService.nextNumber();
                if (number < numberToGuess) {
                    doTransition("incorrectState", new Params().add("message",
                            "Number too low"));
                } else if (number > numberToGuess) {
                    doTransition("incorrectState", new Params().add("message",
                            "Number too high"));
                } else {
                    doTransition("correctState");
                }
            }
        });
    }
}

guessing.ftl

Here's GuessingState's template. It has a form to post the guess back to the state's "guess" EventProcessor. Note the FormTool, from which the template inserts the special form tags, along with fields that JandalFreeMarkerServlet requires. The "number" field corresponds to the "number" parameter that the EventProcessor expects.

${context.getFormTool().getFormOpenTag("guess")}
        Guess a number: <input name="number" type="text" value=""/>
        <br/>
        <br/>
        <input type="submit" value="Submit"/>
${context.getFormTool().getFormCloseTag()}

IncorrectState.java

This is the second State that GameController adds to itself when starting up. Because it's not GameController's initial State, its onEntry method is only called on an incoming transition. Within the method, it outputs the name of its template file and the "message" parameter that was passed in with the transition. It then creates for itself an EventProcessor to transition back to GuessingState on a "playAgain" view-event:

public class IncorrectState extends State {
    public IncorrectState() throws JandalCoreException {
        super("incorrectState");
    }

    public void onEntry() throws JandalCoreException {
        setOutput("template", "incorrect.ftl");
       
        setOutput("message", getParam("message"));

        addViewEventProcessor(new EventProcessor("playAgain") {
            public void onEvent() throws JandalCoreException {
                doTransition("guessingState");
            }
        });
    }
}

incorrect.ftl

Here is the template for IncorrectState. It has a hyperlink that fires the "playAgain" view event.

<b>${context.getOutput("message")}</b>
<br/>
<br/>
<a href="${context.getRequestTool("playAgain").getAction()}">Try Again</a>

CorrectState.java

This is the third State that GameController adds to itself when starting up. Like IncorrectState, it lies dormant until transitioned into, at which point onEntry is called. In that method it adds to itself an EventProcessor which transitions back to GuessingState on a "playAgain" view event:

public class CorrectState extends State {
    public CorrectState() throws JandalCoreException {
        super("correctState");
    }

    public void onEntry() throws JandalCoreException {
        setOutput("template", "guessing.ftl");

        addViewEventProcessor(new EventProcessor("playAgain") {
            public void onEvent() throws JandalCoreException {
                doTransition("guessingState");
            }
        });
    }
}

correct.ftl

Here is the template for CorrectState. It has a hyperlink that fires a "playAgain" view event.

<b>Correct!</b>
<br/>
<br/>
Want to <a href="${context.getRequestTool("playAgain").getAction()}">Guess Again</a>

NumberGuessServiceSet.java

Our ServiceSet plants a NumberGeneratorService inside itself on instantiation:

public class NumberGuessServiceSet extends ServiceSet {
    public NumberGuessServiceSet() {
        addService(new NumberGeneratorService());
    }
}

A ServiceSet basically maps class-names to Service instances.

NumberGeneratorService.java

I have a small confession to make: the number to guess is always 5. Recall that earlier I mentioned Jandal's Service-locking mechanism, and how this Service does not need to be locked. If it did, we would flag it by calling setSynchonized(true) on it within its constructor, then we would need to have a lock on it whenever we want to use it. I'll show all that in another example soon.

public class NumberGeneratorService extends Service {
    public NumberGeneratorService() {
        super();
    }

    public int nextNumber() {
        return 5;
    }
}

But Wait, That's not all

The service layer in this example is not subject to any concurrent modification, so Jandal's nifty Service-locking mechanism is not demonstrated here. For an example of, see the page on Service Locking.

Event flow in this example is solely from view to controller. For an example of events flowing up and down a controller hierarchy, see the page on Event Flow.

Excellently written article,

Sat, 03/28/2009 - 21:09
Zoran (not verified)

Excellently written article, if only all bloggers offered the same content as you, the internet would be a much better place. Please keep it up! Cheers.play roulettepoker reviewsplay blackjackonline video pokerdownload divx movie

Post new comment

The content of this field is kept private and will not be shown publicly.