This document describes and provides instructions for using OpenCloud’s FSM Tool to automate development of state-machine-based services for the Rhino SLEE.

Note The instructions in this section assume familiarity with JAIN SLEE and Rhino development, and the OpenCloud XDoclet tool.

Topics

An overview of finite state machines (FSMs), FSM Tool finite state machine, FSM specifications, the FSM execution model, and how OpenCloud’s FSM Tool implements a FSM for the Rhino SLEE

How FSM works with Rhino, using the FSM specification to code FSMs, current and planned features of the FSM Tool, and a sample FSM design

Steps to download, install, and walk through a sample application developed with FSM Tool

Instructions for developing a component with FSM Tool, including writing the FSM specification, generating an SBB and POJO FSM classes, extending the generated SBB and supporting classes, compiling, packaging, deploying, documenting, and tracing; plus configuring the FSM Tool Ant task for code generation; and descriptions and examples of common patterns in the FSM specification DSL

A reference for the FSM definition language syntax

A reference for the FSM definition language grammar.

Other documentation for the FSM Tool can be found on the FSM Tool product page.

About FSM and the FSM Tool

OpenCloud’s FSM Tool is a lightweight service-creation tool designed to simplify service creation for the Rhino SLEE.

It powers the design, specification, implementation, testing, and documentation of JAIN SLEE Service Building Blocks (SBBs). From the developer’s perspective, the tool ensures that the specification and implementation artifacts remain completely accurate and synchronized throughout the development and maintenance life cycle of each component.

The following overview describes:

What is an FSM?

A finite state machine (FSM) is a behavioural model that consists of:

  • a set of states

  • a set of transitions between those states

  • actions in response to events or transitions.

Example: opening and closing a door

The Wikipedia FSM entry gives the following example of an elevator door opening and closing:

Finite State Machine
225px Finite state machine example with comments.svg
  • The states are Opened and Closed.

  • The transitions are close_door and open_door.

  • The actions are open door and close door.

Moore and Mealy FSMs

FSMs are classified as Moore or Mealy machines. Moore machines use only entry actions, and their output depends only on states. Mealy machines use only input actions, and their output depends on input and states. The illustration at left is an example of a Moore FSM. The same Wikipedia article (referenced at left) gives a related example of a Mealy machine:

Mealy Machine
350px Fsm mealy model door control

While Moore machines are often simpler, Mealy machines often have fewer states.

What is an FSM Tool FSM?

An FSM Tool finite state machine (FSM) is an FSM that implements only the behavioural aspects of a JSLEE component.

Moore meets Mealy

An FSM Tool FSM is also a hybrid state machine model, in that it incorporates features of both Moore and Mealy FSMs. FSM Tool FSMs use three types of actions:

  • input (like a Mealy machine)

  • entry (like a Moore machine)

  • exit (an additional feature, useful for application requirements such as canceling timers).

FSM Tool FSM and communications development

OpenCloud has proven FSM Tool FSM’s effectiveness for large communications systems development. Our software developers have found it to be a flexible and easy-to-use model, with a well-defined language. FSM Tool FSM bridges the gap between the design/requirements/specification and implementation, like this:

  1. Developers write an FSM Tool FSM specification that fully describes the behaviour of a software component.

  2. They directly extend the specified behavioural model, adding implementations of the actions defined in the spec.

  3. The FSM Tool FSM then automatically creates a software executable.

Inputs and actions

FSM Tool FSM inputs and actions define the interface between the behavioural model and the implementation:

  • Inputs are flags — they can be either 'set' or 'unset'.

  • Actions are unparameterized names or references (which are later mapped to specific Java method names in the implementation).

Using inputs and actions, the FSM Tool FSM provides a clean separation between the specification of behaviour and its implementation. This means that software and design evolve together, always in sync.

Endpoints

FSM Tool FSMs can communicate with each other through endpoints. These are named hubs through which an FSM can send or receive inputs. An input sent from an FSM on one of its endpoints, turns into an input received on one of the endpoints of the target FSM.

What’s in an FSM Tool FSM specification?

An FSM Tool FSM Specification consists of the following elements:

Element Description Explanation

States

application states

A finite number of states, each representing a point the application has reached in its execution, and possible future execution steps.

Transitions

conditional expressions of inputs which result in a transition from one state to another

A change from one state to another, plus a guard condition, whereby the transition only happens if the condition is true.

Input actions

actions executed as a function of the current state and inputs

References to logic in the implementation, executed when the FSM is in a particular state, receives a particular input, and a condition evaluates to true.

Exit actions

actions executed on transitioning away from a state

A list of named references to logic in the implementation, executed in the source state at the beginning of a transition.

Entry actions

actions executed on transitioning to a state

A list of named references to logic in the implementation, executed when reaching the sink state at the end of a transition. (Actions executed on transition

Action dictionary

names of actions to be executed

A declared set of actions used in the FSM states' input, exit, and entry actions.

Endpoints

hubs for communication between FSMs

A declared name which can be used when referring to an input, thus indicating that the input is expected to be received through the endpoint from another FSM. Additionally, each endpoint can have an associated SLEE activity context or parent SBB local reference.

Input dictionary

flags raised to indicate the arrival of an event or other condition

A declared set of inputs used in conditional expressions of input actions or transitions. Additionally, inputs can have an associated data object.

Conditions

Boolean algebraic conditional expression

Constructed using AND and OR operators on inputs.

In FSM Tool, there is currently no concept of a NOT operator for conditional expressions. A NOT can be simulated by creating and setting a diametrical input for the input you want to negate.

For example, to negate received\_msg, construct input not\_received\_msg

How does an FSM Tool FSM work?

The FSM Tool FSM execution model works like this:

fsm execution algorithm
  1. Upon execution the FSM checks if it is already being executed by another caller. The FSM returns if the FSM is already executing. Any scheduled inputs will be handled by the current execution of the FSM.

  2. The FSM checks if there are any input changes to the Input Register pending in the scheduler or if a transition has been performed if there was a previous cycle. If there are no new inputs in the scheduled view or transitions, the execution is terminated and the FSM logic awaits a new invocation of the execute() method.

  3. The FSM evaluates possible input actions conditions for the current state and executes the input actions where the condition evaluates to true.

  4. The FSM evaluates any possible transitions.

  5. If there are no transitions possible, the FSM returns to the scheduler/transition checking.

  6. Otherwise, the FSM selects the first transition for which the condition evaluates to true.

  7. It executes exit actions for the source state.

  8. The current state is changed to the transition’s sink state.

  9. The FSM executes entry actions for the sink state.

  10. The FSM then returns to the scheduler/transition checking stage.

Note

During execution, the FSM uses an Input Register to store the currently set inputs (and their associated data objects). The FSM execution algorithm uses two "views" of the Input Register:

  • The execution view does not change during an FSM execution cycle (from the point "Changes scheduled in Input Register or transitioned?" until the FSM execution returns to this point or fails).

  • The scheduler view can change either before or during an execution cycle. If the FSM execution code detects a change to the scheduled view, it will perform another execution cycle by applying the scheduled changes and executing the FSM again.

An FSM cycle will only ever perform a single execution cycle before returning to check for changes to the scheduled view of the input register or if their was a transition.

How the OpenCloud FSM Tool implements a FSM

With OpenCloud’s FSM Tool, developers no longer need to hand code and maintain a complex hierarchy of state machines in Java. The tool formally defines a service’s state machines, in a domain-specific language — and automatically implements state machine behaviour — ready for use in Rhino.

Transient and durable inputs

The FSM Tool implements an FSM using transient and durable inputs:

  • The tool automatically unsets a transient input at the end of an execution cycle.

  • The tool does not automatically unset durable inputs.

Cutting out the interpreter

Traditional implementations of FSMs store the FSM specification in a table structure, and then execute it by using an FSM interpreter. By contrast, the FSM Tool generates Java code from the FSM specification — and compiles it directly. This approach reduces the overhead when compared with an the interpreter approach.

Tip Using FSM Tool, generated code can be as efficient as handwritten.

Support for Moore and Mealy machines

Developers who have experience in Moore and Mealy machines can use FSM Tool to support both machine styles by limiting the use to specific features:

  • Moore machines can be defined using FSM Tool by limiting the specifications to states, transitions, and entry actions.

  • Mealy machines can be defined using FSM Tool by limiting the specifications to states, transitions, and input actions.

Developing SLEE Services with FSM Tool

This chapter provides an overview of using the FSM Tool to develop JAIN SLEE services for the Rhino platform.

Below are descriptions of:

  • How FSM works with Rhino — how Rhino services handle events, and the merits of using FSM Tool versus hand-coding an application’s state machine behaviour

  • Using the FSM Specification to code FSMs — how the spec provides a clean separation of concerns, formalism, and simplicity that make it is easy for developers to create both simple and complex services

  • FSM Tool features — the FSM Tool’s development workflow, showing current and planned features that simplify and enhance coding JAIN SLEE services

  • Example: defining SBB state machines — a hierarchy of state machines for an SBB entity tree design, developed using FSM Tool.

How FSM works with Rhino

Rhino is an event-driven application server for JAIN SLEE services. A typical service must handle many different events from one or more sources (such as an MSC, S-CSCF, or SMSC). With Rhino, an event handler — defined by the service — processes each event a service receives. The event handler then does some logic associated with the event and the current state of the application.

JAIN SLEE does not define the concept of an application state or action. The service developer can choose how best to implement an application’s state machines. Many JAIN SLEE developers hand-code the state-machine logic associated with each application state, and transition directly with the JAIN SLEE APIs.

Warning
The perils of hand-coding

Hand-coding the behavioural model of a Rhino application breaks the link between the specification and its implementation. As a project progresses, the only reliable source of information on the behaviour of the application becomes the application code itself.

How FSM Tool facilitates coding FSMs for Rhino

OpenCloud’s FSM Tool formalizes the definition of an application’s state machine behaviour. At the same time, it provides a framework for implementing and maintaining actions in Java (without having to hand-code the state machine behavioural logic). The tool provides the logic that executes actions when a service receives an event.

Tip
The benefits of using a tool

Using a formalized and executable state machine definition preserves the link between the application’s design and implementation as a project progresses.

Taking a formal approach to state machine specification has some additional benefits. FSM Tool can automatically generate:

  • documentation — The formal state machine specification auto documents the behavioural model of the application component. FSM Tool automatically generates both a tabula state machine description and a diagram, and embeds them in the Javadoc of the Sbb under development. Developers and future maintainers of the application will automatically have well-documented components.

  • testing — Future versions of FSM Tool will introduce automatic generation of a set of JUnit tests for state machine behaviour, and a promela model of the state machine with static analysis on it using Spin model-checking software.

Using the FSM Specification to code FSMs

As described in About FSM and the FSM Tool, the FSM Tool uses a hybrid state machine specification, the FSM Tool Finite State Machine (FSM). Developers specify FSMs for an application, using an easy-to-use domain-specific language (DSL) that explicitly uses state-machine design terminology ("states", "events", "inputs", and "actions").

Warning Some state machine tools, such as UML state charts and SCXML, have complex specifications that include features such as composite states (that represent embedded state machines, include conditional logic and provide embedded programming features). FSM Tool FSM does not try to be a self-contained programming environment. Instead, it focuses on the formal specification of state machine behaviour.

Each FSM Tool FSM specification defines a single state machine. This makes it simple to understand and maintain. The hierarchies of state machines necessary to implement complex application are composed of multiple intercommunicating FSM specifications, which communicate using endpoints and a developer-defined endpoint-to-endpoint mapping logic.

Tip FSM Tool FSM’s clean separation of concerns, formalism, and simplicity make it is easy for developers to create both simple and complex services.

FSM Tool features

The following diagram illustrates a developer’s workflow when using FSM Tool, highlighting existing and planned features of the tool.

design implement cycle

Example: defining SBB state machines

In JAIN SLEE services, a Service Building Block (SBB) may implements a component with one state machine. FSM Tool makes it simple to define the state machine of a single SBB. TIP: For advanced service development FSM Tool provides support for multiple FSMs per SBB.

A typical JAIN SLEE application includes an SBB entity tree. These are very well matched to a service design based on a hierarchy of state machines. SBBs implement each state machine in the hierarchy, and the local object interfaces between the parent and child SBBs provide the mechanism for inter-FSM communication, using endpoints.

The following diagram shows the component design of a service which uses a hierarchy of state machines developed using FSM Tool. The behavioural specification for each component labelled with the <<StateMachine>> stereotype was defined using FSM Tool.

NotifyMeComponents

Quick Start

Below are the steps you need to get started using FSM Tool.

1

Check your prerequisites

Note

To use FSM tool you need:

2

Download FSM Tool

Please see the FSM Tool Download Page.

3

Install FSM Tool

  1. Unpack the downloaded .zip file into $RHINO_HOME. For example:

    cd $RHINO_HOME
    unzip ~/fsm-tool-1.2.0.x.zip
  2. Set the properties rhino.home, slee.version, graphviz.dot, and xdoclet.lib in the $RHINO_HOME/fsmtool/example/build.properties file. For example (enter the correct values for your install environment):

    rhino.home=/RhinoSDK
    
    graphviz.dot=/usr/local/bin/dot
    #graphviz.home=/Program Files/Graphviz2.28
    
    xdoclet.lib=${rhino.home}/opencloud-xdoclet-2.1.1/libs

4

Try the "Hello World" example

Review the sample application that comes with FSM Tool, for an example of how to define a service’s FSM specification (which creates JSLEE service components, by generating an SBB class and extending it to implement state-machine action Java logic).

The multifsmsbb service that comes with FSM Tool is a simple example of two FSMs hosted in a single SBB.

Sample Application

The FSM Tool comes with a sample application, "Hello World", as an example of how to create and extend a service with the tool.

To review the sample application, please explore the following:

What the sample state machine looks like

The FSM Tool sample state machine works like this:

"Hello World" state machine

FsmToolExampleStateMachineSbb

The "Hello World" state machine includes:

  • two states — startState and stopState

  • one endpoint — tcp

  • one input — hello, received through the endpoint tcp

  • one entry action — helloWorld, for stopState.

Note When the machine detects the hello input received on the tcp endpoint in state startState, it transitions to state stopState, whereupon it executes entry action helloWorld.

How the sample state machine is defined

You define FSM Tool state machines using a simple, easy-to-understand (and modify), text-based, domain-specific language (DSL). Below is the DSL definition for the Hello World example (FsmToolExampleStateMachineSbb.fsm):

fsm FsmToolExampleStateMachineSbb {

    description "{product-name} Example State Machine";

    endpoint tcp;

    initial state startState {
         description "Start state";
         transition if(tcp.hello) stopState;
    }
    final state stopState  {
         description "Stop state";
         entryaction helloWorld;
    }

    inputdictionary {
         tcp.hello {
              description "Hello event received.";
         }
    }

    actiondictionary {
         helloWorld {
              description "Say Hello World!";
         }
    }
}

Running the example

Below are instructions to build, deploy, and run the "Hello World" sample FSM Tool service.

Install and start the Rhino SDK

Build and deploy

First build and deploy the service, using the following commands:

cd ${fsmtool.home}/example
ant deploy-example-ra
ant deploy-helloworld

Run with telnet

Then run the service, using OpenCloud’s example RA (which uses a telnet connection to send lines of text as events to the service), as follows:

  1. Start a command window or shell.

  2. Enter telnet localhost 9999

  3. Type hello, and press Return.

What happens…​

When you press Return:

  1. The application wraps the text hello in a new ExampleRA MessageEvent event, and delivers it to FsmToolHelloWorldSbb.

  2. The Sbb receives the event in the onMessageEvent() event handler. The code in the event handler sets the hello input, associating the received event object with the input as data object. It then associates the SLEE ActivityContext on which the event was received with the tcp endpoint and finally triggers a run of the FSM execution algorithm.

    public void onMessageEvent(MessageEvent event, ActivityContextInterface aci) {
    
        // Use the input scheduler to raise the 'hello' input on the 'tcp' endpoint and associate
        // the event object with it
        getInputScheduler().raise(getInputs().tcp.hello, event);
    
        // Associate the aci with the 'tcp' endpoint
        getEndpoints().tcp.setAci(aci);
    
        // Execute the FSM logic
        execute();
    }
  3. The state machine executes, transitioning to stopState and running the helloWorld action. The FSM logic maps the helloWorld action to the method helloWorldAction() which is implemented by the developer to return the "Hello World" text on the telnet connection.

    public void helloWorldAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler,
                                 Endpoints endpoints, Facilities facilities) {
            ...
            // The ACI stored using the endpoint setAci(ActivityContextInterface) method is accessed
            // in the action methods using getAci().
            ActivityContextInterface aci = endpoints.tcp.getAci();
    
            ConnectionActivity connection = (ConnectionActivity) aci.getActivity();
            try {
                connection.sendMessage("Received " + event + ". Hello World.");
    
            } catch (IOException e) {
                facilities.getTracer().warning("Sbb failed to send response", e);
                return;
            }
    }
  4. You receive the "Hello World" text in the telnet connection

    Exit the telnet session (press Ctrl-], type exit and press Return).

Note
Digging deeper

The FSM Tool package includes:

  • full source code of the Hello World example: $\{fsmtool.home\}/example/src/helloworld/com/opencloud/slee/services/fsmtool/helloworld

  • the build scripts for the example: $\{fsmtool.home\}/example/build.xml

When you run the build-service task, it executes the FSM Tool Ant task, to generate an Sbb that implements the state machine definition — FsmToolExampleStateMachineSbb.java. The FsmToolExampleSbb class, which is the root Sbb of the service and extends the FsmToolExampleStateMachineSbb class, implements the event handler method, and provides the implementation for the helloWorld entry action in the helloWorldAction() method.

Extending the "Hello World" example

Now that you have successfully run the "Hello World" example you can extend the state-machine definition to implement more complex state-machine behaviour, as follows:

Extended "Hello World" state machine

FsmToolExampleStateMachineSbb-extended

Extending the example (to respond to subsequent events)

FSM Tool includes an extended "Hello World" example, in $\{fsmtool.home}/example/src/helloworldextended.

Installing the extended HelloWorld example

First build and deploy the service, using the following commands:

cd ${fsmtool.home}/example
ant deploy-example-ra
ant undeploy-helloworld
ant deploy-helloworldextended

Steps to extend from the HelloWorld service

Extend the helloworld example, by editing the FsmToolExampleStateMachineSbb.fsm file in your Java IDE, as follows:

1

Remove the final from the stopState declaration and then add a new transition*

...
    state stopState  {
         description "Stop state";
         entryaction helloWorld;
         transition if(tcp.hello) startState;
    }
    ...

2

Add a new entry action startingAgain to startState, and declare it in the output dictionary

...
    initial state startState {
         description "Start state";
         entryaction startingAgain;
         transition if(tcp.hello) stopState;
    }

    actiondictionary {
         helloWorld {
              description "Say Hello World!";
         }
         startingAgain {
              description "Starting again.";
         }
    }

3

(Re)build the file

ant build-helloworld

The Ant task will re-generate FsmToolExampleStateMachineSbb.java and try compiling the service, which will fail because FsmToolExampleSbb has not yet been updated to implement the new startingAgainAction() method in the actions interface implementation.

4

Implement the startingAgainAction() method in the actions interface implementation.

public void startingAgainAction(Inputs inputs,
                InputScheduler<FSMInput> inputScheduler, Endpoints endpoints,
                Facilities facilities) {

    // The tracer is provided via the facilities method argument
    facilities.getTracer().finest("Running Hello World Action");

    // Objects passed in the InputScheduler.raise(Input, Object) can be accessed
    // from the input using the getAssociatedObject() method
    MessageEvent event = (MessageEvent) inputs.tcp.hello.getAssociatedObject();

    // The ACI stored using the endpoint's setAci(ActivityContextInterface) method is accessed
    // in the action methods using getAci().
    ActivityContextInterface aci = endpoints.tcp.getAci();

    ConnectionActivity connection = (ConnectionActivity) aci.getActivity();
    try {
        connection.sendMessage("Received " + event + ". Starting Again.");
    } catch (IOException e) {
        facilities.getTracer().warning("Sbb failed to send response", e);
        return;
    }
}

5

Recompile and redeploy

ant undeploy-helloworld
ant deploy-helloworld

6

Run the extended example:

  1. Start a command window or shell.

  2. Run telnet localhost 9999

  3. Type hello, and press Return.
    (The application performs same transition as before.)

  4. Type hello, and press Return again.

What happens…​

As a result of your modifications:

  • The state machine executes, transitioning from stopState to startState, and executing the startingAgain action — which returns the text "Starting Again" on the telnet connection.

  • You receive "Starting Again" in the telnet connection.

  • If you keep on entering inputs, you will get alternating messages as the state machine transitions between the two states.

Exit the telnet session (press Ctrl-], type exit and press Return).

Generating Javadocs and diagrams

The FSM Tool supports the automatic generation of javadocs and state-machine diagrams.

Tip The diagrams for the basic and extended example page were automatically generated from their respective state machine definitions.

To generate state machine Javadocs and diagrams, run:

ant javadocs

FSM Tool Procedures

Tip

Work through the "Hello World" example to familiarize yourself with a example of installing, running, and extending a simple Service Building Block (SBB).

Procedures for Multiple FSMs per SBB describes the creation of a Rhino service which hosts multiple POJO FSMs in a single SBB. For anything but simple applications this is the recommended this option is used in combination with FSMs hosted in other SBBs.

Development tasks

To develop a component with FSM Tool:

  • write the FSM specification — use the FSM DSL to write a text file defining states, actions, transitions, inputs, and outputs

  • generate an SBB — use the spec you’ve written to generate SBB code

  • extend the generated SBB — deploy runtime classes, extend the class, map events to inputs, unset durable inputs, and implement action methods

  • compile, package, and deploy — use standard methods to deploy the completed SBB

  • document — use FSM Tool’s features to automatically document the SBB.

  • trace — use FSM Tool’s runtime tracing.

Specify the state machine

First, write the specification for component state machines. In a simple application there may only be one state machine, but in more complex applications there can be many state machines in a hierarchy.

FSM Tool uses the FSM Domain Specific Language (DSL) to specify state machines. The FSM DSL syntax includes:

  • state definitions and descriptions

  • exit, and entry actions associated with each state

  • input actions associated with each state, with conditions that must be met before executing

  • transitions, with conditions that must be met before transitioning

  • an input dictionary, declaring all inputs referenced in transition and input action conditions

  • an action dictionary, declaring all actions that are occurring as input, entry, or exit actions

  • endpoint declarations, naming all the endpoints which are used in conditional expressions and the input dictionary to express where inputs are received from.

Tip For an example of an FSM specification, see the "Definition" tab in the Sample Application. For the full FSM DSL syntax, see the FSM Language Reference.

Generate the FSM SBB Java class

FSM Tool uses the FSM specification to generate an FSM SBB Java class. To correctly generate the code, you need to:

  • Set properties for FSM Tool’s FSM SBB code-generation template.

  • Set up the Ant build.xml file to use the FSM Tool Ant task (see Configuring Code Generation):

    • Add a <fsmtool:generate-fsm> task to the build file, and set the properties of the task for the component under development.

    • Run Ant on the target that includes the <fsmtool:generate-fsm> task.

    • Look for errors that the FSM Tool reports when parsing the specification — correct them, and re-run the Ant target.

Extend the generated SBB

The FSM specification defines the abstract behaviour of the component. To make it work, you must extend the generated FSM StateMachine SBB Java class with an SBB class that:

  • maps received events to raised inputs, and executes the state machine

  • defines, creates and registers an implementation of the actions interface (defined by the StateMachine SBB class) which contains implementations for the FSM’s action methods.

Follow these steps to extend the SBB:

1

Deploy runtime classes

FSM Tool has a small runtime environment. The generated FSM SBB implements the SbbStateMachine interface (see the runtime javadocs). You must deploy the runtime classes with the SBB, either as a library jar or with classes included in the SBB jar. (The "Hello World" example uses the first approach.)

2

Extend the class

The developer-defined SBB class extends the state-machine SBB class that FSM Tool generated, as follows:

public abstract class [fsm-name]Sbb extends [fsm-name]StateMachineSbb {

   ... class body ...


}

3

Map events to state machine inputs

Warning
Think different!

JAIN SLEE developers are accustomed to writing logic in SBB event-handler methods. FSM Tool turns this style on its head, implementing all the service-logic code in action methods. With FSM Tool, you only use event-handler methods for mapping events to inputs, calling getInputScheduler().raise(Input, Object), associating the ActivityContextInterface object with the endpoint, calling getEndpoints().\[endpoint-name\].setAci(aci), and perhaps doing some logging.

When using FSM Tool, you only use an SBB’s event handlers to map events to state-machine inputs, and to call the state machine. The following snippet shows the typical pattern of usage in an SBB event-handler method:

public void onMessageEvent(MessageEvent event, ActivityContextInterface aci) {

        // Use the input scheduler to raise the 'hello' input on the 'tcp' endpoint and associate
        // the event object with it
        getInputScheduler().raise(getInputs().[endpoint-name].[input-name], event);

        // Associate the aci with the 'tcp' endpoint
        getEndpoints().[endpoint-name].setAci(aci);

        // Execute the FSM logic
        execute();
}

You can directly reference the inputs, which are kept internally in the generated state-machine SBB class. For example, if the declared input is hello and received on endpoint tcp, the service calls the getInputScheduler().raise(getInputs().tcp.hello, event) method to raise the input and associate the handled SLEE event object with the input as data object.

If you need to set more than one input before executing the state machine, simply add more getInputScheduler().raise(…​) methods.

Finally, trigger the FSM execution cycle by calling execute(), which runs the state machine and executes any actions.

4

Unset durable inputs

Inputs are transient by default, in the state machine specification input dictionary. (FSMs automatically clear transient inputs after one execution cycle.) If you need to use durable inputs, you will need to manually unset them within an action method, as follows:

public void helloWorldAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities) {

      inputScheduler.clear(inputs.tcp.hello);
}

5

Implement actions interface

Each action defined in the state machine specification renders into a method declaration in an actions interface, which is declared as an inner interface of the state-machine SBB class.

public interface FsmToolExampleActions extends Actions{

      /** helloWorld */
      public void helloWorldAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities);
}

The actions interface must be implemented in the extended SBB, and an instance must be registered with the state machine execution class (best to do this in the setSbbContext() method). Action methods execute all logic associated with an event’s arrival. For example, the following code snippet shows the method implementation for the helloWorld action, defined in the "Hello World" example state machine specification:

...
 //implementing the actions interface
private class ActionsImplementation implements FsmToolExampleActions {

    public void helloWorldAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities) {

        //this is the action implementation for action 'helloWorld' from the specification
        ActivityContextInterface aci = endpoints.tcp.getAci();
        ConnectionActivity connection = (ConnectionActivity) aci.getActivity();
        try {
            connection.sendMessage("Received " + event + ". Hello World.");
        }
        catch (IOException e) {
            facilities.getTracer().warning("Sbb failed to send response", e);
            return;
        }
    }
}
...
 //registration of an instance with the state-machine executor
public void setSbbContext(SbbContext context) {
    super.setSbbContext(context);
    registerActionImplementation(new ActionsImplementation());
}
Tip
XDoclet

We recommend using OpenCloud’s XDoclet tool to generate deployment descriptors for the SBB (as the generated SBB class declares CMP fields using XDoclet tags).

If you don’t use XDoclet, you’ll need to maintain deployment descriptors for the extended SBB by hand.

Compile, package, and deploy the SBB

Tip
Follow standard procedures

Once you’ve finished the steps to implement an SBB, use the Ant tasks to compile, package, and deploy the service. See the Sample Application.

Generate documentation

To create a graphic illustration of the state machine, configure a <fsmtool:generate-dot> task to generate the Graphviz dot file format (see the "Hello World" example for details).

FSM Tool uses code-generation templates to specify details of the implementation that a state-machine specification generates. The output of code generation does not depend on the FSM Tool directly — by changing only the templates, completely different output formats can be produced. This means that you can use FSM Tool to generate many different types of output file from the specification. For example, FSM Tool comes with the following templates for generating javadocs and graphics from the state machine specification:

  • fsm-for-multi-per-sbb.stg — to generate a POJO FSM when multiple FSMs are required in a single SBB

  • multi-fsm-sbb.stg — to generate an SBB Java class used to host one or more POJO FSMs

  • fsm.stg — to generate an FSM SBB Java class supporting a single FSM

  • dot.stg — to generate a GraphViz dot file.

Note The Javadoc of the generated SBB makes reference to the Graphviz state-machine image. You can also reference the state-machine image in the Javadoc of the SBB that extends the generated class, by including the image there.

FSM Tool can use the templates provided, use enhanced templates to meet specific requirements, or use templates created from scratch. The templates are written in StringTemplate template group format (see the StringTemplate documentation for details of file syntax). Any code generation template group used by FSM Tool must implement the full set of template names used in the fsm.stg template group file. This set of template names is the interface between the template group and the FSM Tool. The template implementations can generate any text output or empty strings that the final output format requires (.java, .dot, .html, and so on).

Enable runtime tracing

The generated FSM SBB can be configured to produce state-transition tracer messages, by setting the tracer level for the deployed SBB to "Fine". Either the "" ({root}) tracer name or the "fsm" tracer name for the SBB can be used.

Once the tracer is set, trace messages such as following are written to the rhino logging subsystem:

2010-08-18 10:38:16.340  Fine    [trace.HelloWorldService_0_1.fsm]   <jr-2> [HelloWorldService/FSM1_Sbb]  pkey=101:182573003122:0 FSM[transition=CheckParam->PrintHW, InputRegister[scheduled=[], execution=[local_printHW]], Endpoints[Endpoint[name="local,aci=[not-set,sbb-not-attached],eventContext=[not-set],endpointInterface=[not-set],activityContextInterfaceFactory=[not-set],sbbLocalObject=[not-set]],Endpoint[name="ep,aci=[set,sbb-attached],eventContext=[not-set],endpointInterface=[not-set],activityContextInterfaceFactory=[not-set],sbbLocalObject=[not-set]]]]

Action tracing (as well as transition tracing) can be enabled by setting the trace level of the SBB to "Finest". Here is an example of action logging output:

2010-08-18 10:40:44.081  Finest  [trace.HelloWorldService_0_1.fsm]   <jr-3> [HelloWorldService/FSM1_Sbb]  pkey=101:182573003122:0 FSM[action=checkParams, state=CheckParam, result=executed successfully]

2010-08-18 10:40:44.081  Fine    [trace.HelloWorldService_0_1.fsm]   <jr-3> [HelloWorldService/FSM1_Sbb]  pkey=101:182573003122:0 FSM[transition=CheckParam->PrintHW, InputRegister[scheduled=[], execution=[local_printHW]], Endpoints[Endpoint[name="local,aci=[not-set,sbb-not-attached],eventContext=[not-set],endpointInterface=[not-set],activityContextInterfaceFactory=[not-set],sbbLocalObject=[not-set]],Endpoint[name="ep,aci=[set,sbb-attached],eventContext=[not-set],endpointInterface=[not-set],activityContextInterfaceFactory=[not-set],sbbLocalObject=[not-set]]]]

2010-08-18 10:40:44.082  Finest  [trace.HelloWorldService_0_1.fsm]   <jr-3> [HelloWorldService/FSM1_Sbb]  pkey=101:182573003122:0 FSM[action=printHelloWorld, state=PrintHW, result=executed successfully]

Configuring Code Generation

FSM Tool includes Ant tasks for seamlessly integrating code generation with existing build infrastructure.

Below are:

Declaring the Ant tasks

The FSM Tool Ant tasks are:

  • fsmtool:make-multi-fsm-sbb — generates an SBB Java class to host one or more generated Java classes for each state-machine specification.

  • fsmtool:generate-fsm — generates SBB Java class for the state-machine specification.

  • fsmtool:generate-dot — generates a GraphViz compatible dot file that visualises the state machine diagram.

The FSM Tool Ants task must be declared in the Ant build.xml file using a <taskdef> tag. The FSM Tool ships with the fsmtool.jar file, which contains the Ant tasks. You need to install it in a path referenced by the <taskdef>. In the "Hello World" example, the jar file is in the ${basedir}/lib directory. To declare the tasks, add an fsmtool namespace attribute to the <project> tag, and then use the <taskdef> to pick up all task declarations from the antlib.xml file (which is contained in the fsmtool.jar).

<project name="{product-name} Example Service" default="help" basedir="." xmlns:fsmtool="antlib:com.opencloud.sce.fsmtool.ant">
...
    <target name="init" depends="management-init">

        <path id="fsmtool.classpath">
            <fileset dir="${basedir}/../libs" includes="*.jar"/>
        </path>
        <taskdef uri="antlib:com.opencloud.sce.fsmtool.ant"
                 resource="com/opencloud/sce/fsmtool/ant/antlib.xml"
                 classpathref="fsmtool.classpath"/>
        ...
    </target>
...
</project>

Ant task parameters

The make-multi-fsm-sbb, generate-fsm and generate-dot tasks use the following attributes.

make-multi-fsm-sbb

Attribute Description Type
sbbClassName

Class name for the generated SBB class.

Required

package

Package name for the output.

Required

destDir

Destination directory for the generated classes; for example:

${basedir}/src/helloworld/com/opencloud/slee/services/fsmtool/helloworld

Required

tracerName

The prefix used for trace messages.

Required

Element Description Type
fsmtool:fsm

FSM specification details.

One or more required

The fsmtool:fsm element has the following attributes:

Attribute Description Type

           

specification

Filename of the FSM specification from which to generate code; for example:

${basedir}/src/helloworld/com/opencloud/slee/services/
fsmtool/helloworld/FsmToolExampleStateMachineSbb.fsm
Required

fsmClassname

Class name for the generated FSM class.

Required

fsmFullClassname

Package path and class name for the generated FSM class.

generate-fsm

Attribute Description Type
fsmSpecificationFilename

Filename of the FSM specification from which to generate code; for example:

${basedir}/src/helloworld/com/opencloud/slee/services/
fsmtool/helloworld/FsmToolExampleStateMachineSbb.fsm

Required

fsmTemplateFilename

Path and filename of the template to use for code generation; for example:

templates/fsm.stg
Note If the file cannot be found in the given path and filename combination on the filesystem, the task automatically checks if the file exists as aresource contained in the fsmtool.jar. The standard fsm.stg template is a resource in the fsmtool.jar. Set fsmTemplateFilename="templates/fsm.stg" to use it.

Required

package

Package name for the output.

Required

fileType

Suffix for the generated file. For fsm.stg, must be set to java.

Required

class

Class name for the generated code.

Required

superClass

A Java class which will be the super class of the generated state-machine SBB. If set, a fully qualified class name is required.

Optional

outputRootDirectory

The root directory used to write the generated class file. The package subdirectory structure will be created automatically.

Required

tracerName

The prefix used for trace messages.

Required

generate-dot

Attribute Description Type
fsmSpecificationFilename

Filename of the FSM specification from which to generate code; for example:

${basedir}/src/helloworld/com/opencloud/slee/services/
fsmtool/helloworld/FsmToolExampleStateMachineSbb.fsm

Required

fsmDotTemplateFilename

Path and filename of the template to use for the dot-file generation; for example:

${basedir}/templates/fsm.dot
Note If the file cannot be found in the given path and filename combination on the filesystem, the task automatically checks if the file exists as aresource contained in the fsmtool.jar. The standard dot.stg template is a resource in the fsmtool.jar. Set fsmTemplateFilename="templates/dot.stg" to use it.

Required

outputFile

Path and filename to write the resulting state-machine dot-diagram code to.

Required

Examples for code-generation templates

Below are examples of FSM Tool Ant task definitions using the fsm.stg and dot.stg templates, from the "Hello World" sample application.

fsm-for-multi-per-sbb.stg and multi-fsm-sbb.stg

Using the fsm-for-multi-per-sbb.stg and multi-fsm-sbb.stg templates to generate the state-machine and hosting SBB Java class files:

<fsmtool:make-multi-fsm-sbb
          sbbClassName="FsmToolMultiFsmExampleStateMachineSbb"
               package="com.opencloud.slee.services.fsmtool.multifsmsbb"
               destDir="${basedir}/generated/multifsmsbb/com/opencloud/slee/services/fsmtool/multifsmsbb"
            tracerName="multifsmsbb">

     <fsmtool:fsm specification="${basedir}/src/multifsmsbb/com/opencloud/slee/services/fsmtool/multifsmsbb/MessageFSM.fsm"
                   fsmClassname="MessageFSM"
               fsmFullClassname="com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM"/>

     <fsmtool:fsm specification="${basedir}/src/multifsmsbb/com/opencloud/slee/services/fsmtool/multifsmsbb/TimerFSM.fsm"
                   fsmClassname="TimerFSM"
               fsmFullClassname="com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM"/>

</fsmtool:make-multi-fsm-sbb>

fsm.stg

Using the fsm.stg template to generate the state-machine SBB Java class file:

<fsmtool:generate-fsm
        fsmSpecificationFilename="${basedir}/src/helloworld/com/opencloud/slee/services/fsmtool/helloworld/FsmToolExampleStateMachineSbb.fsm"
        fsmTemplateFilename="templates/fsm.stg"
        package="com.opencloud.slee.services.fsmtool.helloworld"
        class="FsmToolExampleStateMachineSbb"
        fileType="java"
        outputRootDirectory="${basedir}/generated/helloworld"
        traceName="helloworld"/>

dot.stg

Using the dot.stg template to generate a GraphViz dot-format file from the state-machine specification:

<fsmtool:generate-dot
        fsmSpecificationFilename="${basedir}/src/helloworld/com/opencloud/slee/services/fsmtool/helloworld/FsmToolExampleStateMachineSbb.fsm"
        fsmDotTemplateFilename="templates/dot.stg"
        outputFile="${basedir}/doc/javadoc/helloworld/com/opencloud/slee/services/fsmtool/helloworld/FsmToolExampleStateMachineSbb.dot"/>

FSM Debugging

When an unchecked exception is thrown in an FSM action method, the FSMs on the call path will aggregate the current status of each FSM in an FSMExecutionException.

This information is useful for debugging runtime issues in FSM-based Rhino services.

The following exception was generated by a modified version of the multifsmsbb example provide in the FSM Tool examples:

com.opencloud.sce.fsmtool.FSMExecutionException:
FSM            = TimerFSM
previous state = waitForTimerExpiry
current state  = notifyExpiry
action         = notifyOnTimerExpiry(entry)
input register = InputRegister[scheduled=[], execution=[local_timerExpiry]]
endpoints      = Endpoints[Endpoint[local,aci=[set,sbb-not-attached]],Endpoint[timer]]
--------------------------------------------------
FSM            = MessageFSM
previous state = waitForTimer
current state  = sendResponse
action         = sendResponseOnTimerExpiry(entry)
input register = InputRegister[scheduled=[], execution=[timer_timerExpiry]]
endpoints      = Endpoints[Endpoint[local],Endpoint[tcp,aci=[set,sbb-attached]],Endpoint[timer]]
--------------------------------------------------
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM.execute(MessageFSM.java:140)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.FsmToolMultiFsmExampleSbb$2.timerExpired(FsmToolMultiFsmExampleSbb.java:68)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSMActionsImpl.notifyOnTimerExpiryAction(TimerFSMActionsImpl.java:63)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM$Action$2.execute(TimerFSM.java:399)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM.executeAction(TimerFSM.java:251)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM.access$1000(TimerFSM.java:39)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM$3.executeEntryActions(TimerFSM.java:619)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM$3.executeEntryActions(TimerFSM.java:600)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM.executeFsm(TimerFSM.java:211)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.TimerFSM.execute(TimerFSM.java:125)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.FsmToolMultiFsmExampleSbb.onTimerEvent(FsmToolMultiFsmExampleSbb.java:122)
        at com.opencloud.rhino.deployed.sbb.OpenCloud.multifsmsbb_sbb_1_1.SbbOCBBBean.sbbDeliverEvent(SbbOCBBBean.java:327)
        at com.opencloud.deployed.Service_multifsmsbb_OpenCloud_1_1_2.SBB_multifsmsbb_sbb_OpenCloud_1_1OCBB_Local.sbbDeliverEvent(SBB_multifsmsbb_sbb_OpenCloud_1_1OCBB_Local.java:1122)
        ... 7 more
Caused by: java.lang.IllegalStateException: Transition Action Error
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM.executeAction(MessageFSM.java:272)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM.access$900(MessageFSM.java:39)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM$3.executeEntryActions(MessageFSM.java:670)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM$3.executeEntryActions(MessageFSM.java:651)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM.executeFsm(MessageFSM.java:211)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM.execute(MessageFSM.java:125)
        ... 19 more
Caused by: java.lang.IllegalArgumentException: Intentional exception
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSMActionsImpl.sendResponseOnTimerExpiryAction(MessageFSMActionsImpl.java:68)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM$Action$3.execute(MessageFSM.java:434)
        at com.opencloud.slee.services.fsmtool.multifsmsbb.MessageFSM.executeAction(MessageFSM.java:251)
        ... 24 more

Other FSM Tasks

Below are descriptions and examples of common patterns in FSM code for:

  • event-parameter checking — defining an input action for a state, for example to verify that message parameters are correct before transitioning to another state

  • error handling — setting an error input that transitions the state machine to an error state

  • multiple-SBB applications — managing multiple SbbLocalObject calls for JSLEE services with several SBBs

  • timers — starting, canceling, or handling a timer event (for applications where the network might not return a response).

Event-parameter checking

To check event parameters, applications typically use input actions. For example, when a service receives an InitialDP or SIP INVITE, it may need to verify that message parameters or headers are correct for the application, before transitioning to another state for further processing. You can implement this type of parameter checking by defining an input action for the state. For example:

initial state startState {
         transition if(capv2.idp) checkingInitialDP;
    }

    state checkingInitialDP  {
         description "check the Initial DP";
         entryaction checkIDP;
         inputaction if(invalidParameter) incrementInvalidParameterCalls;
         transition if(invalidParameter) errorState;
         transition connectCallState;
    }

In this example, when the state machine receives an idp on the endpoint capv2, it:

  • transitions from startState to checkingInitialDP

  • executes entry action checkIDP

    • sets the invalidParameter input if it finds a parameter to be incorrect

    • checks if another iteration can be performed

    • checks input actions

    • executes the incrementInvalidParameterCalls action if input invalidParameter is set

    • transitions to errorState if invalidParameter is set

    • transitions to connectCallState if invalidParameter isn’t set.
      (This transition is unconditional, so will always be executed.)

Note The state machine performs two transitions, startStatecheckingInitialDPerrorState (or connectCallState), with only one call to execute() for the idp. The state machine only stops when no changes occur any more to the scheduled view of the Input Register.

Error handling

Tip The recommended way of handling an error during action execution is to set an error input that transitions the state machine to an error state — this makes the error-handling behaviour explicit in the FSM specification.

For example:

public void sendReleaseCallAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities) {

    ... action implementation goes here...

    if (error_detected) {
        inputScheduler.raise(inputs.local.internalError);
        return;
    }

}

The associated state has the following pattern:

state releaseCallState  {
         description "Release call state";
         entryaction sendReleaseCall;
         transition if(internalError) errorState;
    }

In this example, when the FSM sets the internalError in the sendReleaseCall entry action, it transitions to the errorState state.

Multiple FSMs

Many JSLEE services comprise multiple SBBs, and you can implement their FSMs with FSM Tool. The recommended way of communication between SBBs are synchronous calls across SbbLocalObject interfaces. If the child FSM needs to communicate a value back to the calling parent FSM after executing its action methods, this can be achieved in two ways:

1. Set the response in a local instance field, and return it after the execute() method has been called.

For example:

public abstract ChildSbb extends ChildStateMachineSbb {
...
     private Response response;

     // SBB local method called by parent FSM through the child's SBB local interface
     public Response childCallLocalMethod(Object dataFromParent) {

              //set an input in the child's FSM, associated with the
              //data object provided by the parent
              getInputScheduler(getInputs().tcp.childCall, dataFromParent);

              //trigger execution of the child FSM
              execute();

              //return the computed value to the parent FSM
              Response returnValue = response;
              response = null;
              return returnValue;
     }

     private class ActionImplementation extends ChildActions {
         public void helloWorldAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities) {

             ...

             response = new Response(responseData);
         }
     }

2. Use re-entrant call to the parent SBB.

For example:

public abstract ChildSbb extends ChildStateMachineSbb {
...
     // SBB local method called by parent FSM through the child's SBB local interface
     public void childCallLocalMethod(CallbackInterface parentCallbackInterface, Object dataFromParent) {

              //set an input in the child's FSM, associated with the
              //data object provided by the parent
              getInputScheduler(getInputs().tcp.childCall, dataFromParent);

              //associate the provided callback interface to the parent Sbb
              //with the endpoint on which the input was received
              getEndpoints().tcp.setSbbLocalObject(parentCallbackInterface);

              //trigger execution of the child FSM
              execute();
     }

     private class ActionImplementation extends ChildActions {
         public void helloWorldAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities) {
              ...
              //retrieve the callback reference from the endpoint
              CallbackInterface parentCallback = ((CallbackInterface)endpoints.tcp.getSbbLocalObject());

              //send the response back to the parent via the parent's Sbb local method
              Response response = new Response(responseData);
              parentCallback.sendResponse(response);
         }
     }
Tip
Approach 1 (using local instance fields) is recommended for most situations

If you use re-entrant callbacks, remember to set the attribute reentrant="True" in the parent SBB’s xdoclet tags (respectively deployment descriptor). For example:

...
/*
* @slee.sbb id="... name="... vendor="... reentrant="True" ...
* ...
*/
public abstract class ParentFSMSbb extends ParentFSMVsaSbb { ...
Note Channels between JSLEE components can be synchronous (using the SbbLocalObject interfaces between SBBs) or asynchronous using activity-context interfaces (ACIs). Future releases of FSM Tool may use a channel model, with an ACI channel and a local channel, so action methods can access channels directly from passed parameters.

Timers

Applications need timers when networks might not return a response. For example, the following snippet from a FSM specification contains a state to start, cancel, or handle an timer event:

state waitForMTForwardSM {
        entryaction sendMTForwardSM, startTimer;
        exitaction cancelTimer;
        transition if(MTForwardSMConf) nextState;
        transition if(Timer) failure;
      }

In this example, the FSM uses:

  • entry action startTimer to start the JSLEE timer

  • exit action cancelTimer to cancel the timer started in startTimer.

If the timer expires, the Sbb receives a TimerEvent through its onTimer() event handler method. It sets the Timer input and calls the execute() method. For example:

/** * @slee.event-method * initial-event="False" * event-type-name="javax.slee.facilities.TimerEvent" * event-type-vendor="javax.slee" * event-type-version="1.0" */
      public void onTimer(TimerEvent event, ActivityContextInterface aci) {

            getInputScheduler().raise(getInputs().timerEndpoint.Timer, event);
            getEndpoints().timerEndpoint.setAci(aci);
            execute();
      }
Tip You implement the startTimer and cancelTimer actions using normal JSLEE timer-handling code.

Procedures for Multiple FSMs per SBB

To develop a component with FSM Tool:

Specify the state machines

When creating multiple FSMs per SBB the process is the same as for bSBB FSM specification.

The FSM Tool installation has an example called multifsmsbb. This example has two FSMs:

  • MessageFSM — handles text string requests in seconds for delay before responding

  • TimerFSM — handles the timer for delayed responses.

Generate the FSM POJO classes and hosting SBB class

FSM Tool uses the FSM specifications to generate an FSM POJO classes and SBB class. To correctly generate the code:

  • Set up the Ant build.xml file to use the FSM Tool Ant task (see make-multi-fsm-sbb):

    • Add a <fsmtool:make-multi-fsm-sbb> task to the build file, and set the properties of the task for the component under development.

    • Run Ant on the target that includes the <fsmtool:make-multi-fsm-sbb> task.

    • Look for errors that the FSM Tool reports when parsing the specification — correct them, and re-run the Ant target.

Implement the necessary classes

The FSM specification defines the abstract behaviour of the component. To make it work, you must:

  • create an SBB class which maps received events to raised inputs, and executes the state machines which have been registered. The SBB must extend the generated FSM host SBB.

  • define, create and register implementations of the actions interfaces (defined by a generated Actions interfaces) which contain implementations for the FSM’s action methods.

Here are the required steps:

1

Deploy runtime classes

FSM Tool has a small runtime environment. The generated FSM SBB implements the SbbStateMachine interface (see the runtime javadocs). You must deploy the runtime classes with the SBB, either as a library jar or with classes included in the SBB jar.

2

Extend the generated FSM hosting SBB

The developer-defined SBB class extends the generated FSM hosting SBB class, as follows:

public abstract class [fsm-name]Sbb extends [fsm-name]StateMachineSbb {

  ... class body ...


}

3

Wire up and register the actions implementation classes

The FSMs are wired up to support interaction between the FSMs. The FSM action implementation POJO classes are registered to the generated FSMs.

@Override
    public void setSbbContext(SbbContext context) {
        super.setSbbContext(context);

        // Wire up the FSMs
        Timer timer = new Timer() {
            @Override
            public void setTimer(Integer interval) {
                getTimerFSM().getInputScheduler().raise(getTimerFSM().getInputs().timer.setTimer, interval);
                getTimerFSM().execute();
            }
        };

        MessageFSMCallBack callback = new MessageFSMCallBack() {
            @Override
            public void timerExpired() {
                getMessageFSM().getInputScheduler().raise(getMessageFSM().getInputs().timer.timerExpiry);
                getMessageFSM().execute();
            }
        };

        // Register an instance of the inner class which implements the actions implementation
        getMessageFSM().registerActionImplementation(new MessageFSMActionsImpl(timer));
        getTimerFSM().registerActionImplementation(new TimerFSMActionsImpl(callback));
    }

4

Map events to state machine inputs

Create SBB’s event handlers to map events to state-machine inputs, and to call the state machines. The following snippet shows the typical pattern of usage in an SBB event-handler method in a FSM hosting SBB:

/** * The RA has received a message on a {@link ConnectionActivity}. * * @slee.event-method * initial-event="True" * event-type-name="com.opencloud.slee.resources.example.MessageEvent" * event-type-vendor="OpenCloud" * event-type-version="2.2" * initial-event-select-variable="ActivityContext" */
    public void onMessageEvent(MessageEvent event, ActivityContextInterface aci) {

        // Use the input scheduler to raise the 'message' input on the 'tcp' endpoint and associate
        // the event object with it
        getMessageFSM().getInputScheduler().raise(getMessageFSM().getInputs().tcp.message, event);

        // Associate the aci with the 'tcp' endpoint
        getMessageFSM().getEndpoints().tcp.setAci(aci);

        // Execute the FSM logic
        getMessageFSM().execute();
    }

You can directly reference the inputs, which are kept internally in the generated state-machine SBB class. For example, if the declared input is message and received on endpoint tcp, the service calls the getMessageFSM().getInputScheduler().raise(getInputs().tcp.message, event) method to raise the input and associate the handled SLEE event object with the input as data object.

If you need to set more than one input before executing the state machine, simply add more getMessageFSM().getInputScheduler().raise(…​) methods.

Finally, trigger the FSM execution cycle by calling getMessageFSM().execute(), which runs the state machine and executes any actions.

In this example there is a second FSM called TimerFSM. It is accessed using getTimerFSM().

5

Implement actions interfaces

Each action defined in the state machine specification renders into a method declaration in an actions interface, which is declared as an inner interface of the state-machine POJO class. In this case TimerFSM.TimerFSMActions:

public class TimerFSM  implements MultiFsmSbbStateMachine<TimerFSM.ReturnCode> {
    ...

    public interface TimerFSMActions extends Actions{

        /** Set the slee timer */
        public void setTimerAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities);

        /** Send a response message on timer expiry */
        public void notifyOnTimerExpiryAction(Inputs inputs, InputScheduler<FSMInput> inputScheduler, Endpoints endpoints, Facilities facilities);

    }

    ...
}

The action interfaces are typically implemented in POJO classes, and an instance must be registered with the state machine execution class (best to do this in the setSbbContext() method). Action methods execute all logic associated with an event’s arrival. For example, the following code snippet shows the method implementation for the setTimer action, defined in the multifsmsbb example:

...
    //implementing the actions interface
    public void setTimerAction(Inputs inputs,
                               InputScheduler<FSMInput> inputScheduler,
                               Endpoints endpoints,
                               Facilities facilities) {

        // The tracer is provided via the facilities method argument

        // Reuse or initialize the timer Null Activity
        ActivityContextInterface nullAci = endpoints.timer.getAci();
        if (nullAci == null) {
            NullActivity nullA = facilities.getNullActivityFactory().createNullActivity();
            nullAci = facilities.getNullACIFactory().getActivityContextInterface(nullA);

            // Attach to the ACI to receive timer expiry events
            nullAci.attach(facilities.getSbbLocalObject());
            endpoints.local.setAci(nullAci);
        }

        int interval = 5000;
        if(inputs.timer.setTimer.getAssociatedObject() instanceof Integer) {
            interval = (Integer)inputs.timer.setTimer.getAssociatedObject();
        }

        long fireTime = System.currentTimeMillis() + interval;

        TimerID timerID = facilities.getTimerFacility()
                                    .setTimer(nullAci,
                                              null,
                                              fireTime,
                                              DEFAULT_TIMER_OPTIONS );
    }
    ...

Compile, package, and deploy the SBB and supporting POJO classes

Tip
Follow standard procedures

Once you’ve finished the steps to implement an classes, use the Ant tasks to compile, package, and deploy the service. See the multifsmsbb service in the examples.

Generate documentation

To create a graphic illustration of the state machine, configure a <fsmtool:generate-dot> task to generate the Graphviz dot file format (see the "Hello World" example for details).

FSM Tool FSM DSL Syntax

This page defines the FSM Tool specification language syntax.

Tip For the specification language grammar, please see FSM Tool FSM DSL Grammar.

FSM specification syntax

You define an FSM specification with the fsm keyword, followed by the name of the FSM, and then a body contained by { and }. For example:

fsm NameOfFsmSpecification {

    ... fsm specification body ...

}

FSM specification body

The body of the FSM specification has the following elements:

description

The description element describes the FSM, using the description keyword followed by the text in quotes (which can span multiple lines):

description "text of the description of the fsm";

state

The state element defines states in the FSM, including actions and transitions.

Keyword + name of state + body Initial and final states

You define a state with the state keyword, followed by the name of the state, and then the state body (see below).

state someState {
        ... state body ...
      }

You define initial and final states using the initial and final modifiers. For example, for an initial state:

initial state startState {
          ... state body ...
        }
Tip Only one initial state is permitted.
state body
Warning The order of declaration of elements must be in the order they are listed below.

The state body has the following elements, defined by keywords and other variables, and declared in the following order:

Element Keyword + …​ Example Number

description

description

description "text of the description of the state";

entry actions

entryaction + comma-separated list of actions

entryaction setTimer, sendMOForwardSM;

0 or 1

exit actions

exitaction + comma-separated list of actions

exitaction cancelTimer;

0 or 1

input actions

inputaction + condition & comma-separated list of actions

inputaction if(tcp.hello) checkWeather;
inputaction if(tcp.hello && niceWeather) checkTemperature;
  • The first line in this example evaluates the conditional expression tcp.hello; and if true, executes the checkWeather action.

  • The second line evaluates the conditional expression tcp.hello && niceWeather; and if the external tcp.hello and local niceWeather inputs are both true, executes the checkTemperature action.

  • The if() statement must contain a conditional expression.

0 or more

transitions

transition + condition & name of the sink state

transition if(tcp.hello) stopState;
transition if(capv2.idp && notbarred) continueCall;
  • The first line in this example evaluates the conditional expression tcp.hello; and if true, performs the transition and executes any actions as per the FSM execution model.

  • The second line evaluates the conditional expression capv2.idp && notbarred; and if true, performs the transition to the continueCall state.

transition continueCall;
  • This example shows an unconditional transition. The state machine will never wait for input in this state as this transition will be performed. Only one unconditional transition should be declared and always last. Any transitions declared after it will be unreachable and never checked or performed.

  • For multiple transitions with conditions evaluating to true, the first transition declared will take precedence and will be the only transition to execute.

  • The if() statement must contain a conditional expression.

0 or more

endpoint

The endpoint element declares the name of an endpoint for the FSM:

endpoint tcp;

inputdictionary

The inputdictionary must declare all inputs used in the state body elements. You define an inputdictionary element with the inputdictionary keyword, followed by the inputdictionary body.

inputdictionary {

        ... inputdictionary body ...

      }
inputdictionary body

The inputdictionary body declares 0 or more inputs. Each input can either be an external input (received through an endpoint) or a local input (internal to the current FSM).

External inputs are declared by the name of the endpoint on which they are received, followed by a ., the name of the input and finally a body enclosed in { and }:

tcp.hello {

            ... input declaration body ...

          }

Local inputs are declared simply by the name of the input and followed by a body enclosed in { and }:

niceweather {

            ... input declaration body ...

          }

Each input declaration body can contain the following elements:

  • description — keyword followed by text in quotes.

  • type — defined with the type keyword, followed by either transient or durable; defaults to transient if not specified; determines whether you need to manually unset the input (if durable) or whether the input is cleared automatically at the end of the FSM execution cycle (see "Extend the generated SBB" in FSM Tool Procedures).

  • send — defiend with the send keyword, followed by an input name typically an endpoint related input. Used to specify the behaviour of an action.

Note Local inputs are declared in the FSM DSL without endpoint names. However, when referring to them when writing Java code in the implementation class, they are accessed through a default endpoint called local which is present in all FSMs.

actiondictionary

The actiondictionary must declare all entry, exit and input actions used in state body elements. An actiondictionary element is defined with the actiondictionary keyword, followed by the actiondictionary body.

actiondictionary {
        ... actiondictionary body ...
      }
actiondictionary body

The actiondictionary body declares 0 or more actions, each with an action name followed by a body enclosed in { and }.

setTimer {

      ... action declaration body ...

    }

Each action declaration body can contain the following elements:

  • description — keyword followed by text in quotes.

Conditional expressions

Conditional expressions consist of:

  • Variables (inputs)

  • AND (&&) and OR (||) operators

  • NOT (!) operator

  • parentheses to specify the order of precedence — if not specified, && (conditional AND) takes precedence over ||(conditional OR). ! (NOT) takes precedence over && and ||.

For example:

tcp.hello
    tcp.hello && niceWeather
    !niceWeather
    capv2.idp
    capv2.idp && parametersCorrect
    (capv2.idp && authenticated) || (capv2.idp && authenticationNotRequired)

In the example, tcp.hello and capv2.idp are both external inputs, and niceWeather, parameterCorrect, authenticated, and authenticationNotRequired are all local inputs.

FSM Tool FSM DSL Grammar

This page defines the FSM Tool specification language grammar.

Tip For the specification language syntax, please see FSM Tool FSM DSL Syntax.

Below is a formal grammar reference for the FSM Tool Finite State Machine Domain Specific Language (DSL), generated as Extended Backus-Naur Form (EBNF) in ANother Tool for Language Recognition (ANTLR) notation.

fsm
    :   'fsm' ID '{' fsm_body '}'
    ;

fsm_body
    :   description?
        endpoint*
        state+
        inputdictionary?
        actiondictionary?
    ;

endpoint
    :   'endpoint' ID ';'
    ;

state
    :    statetype? 'state' ID '{' state_body '}'
    ;

state_body
    :   description? entryaction? exitaction? inputaction* transition*
    ;

description
    :   'description' QUOTED ';'
    ;

statetype
    :   ('initial' ('final')?|'final')
    ;

entryaction
    :   'entryaction' actionlist ';'
    ;

exitaction
    :   'exitaction' actionlist ';'
    ;

actionlist
    :   ID (',' ID )*
    ;

inputaction
    :   'inputaction' 'if' '(' conditionalExpression ')' actionlist ';'
    ;


parConditionalExpression
    :   '(' conditionalExpression ')'
    ;

conditionalExpression
    :   conditionalAndExpression ('||' conditionalExpression )?
    ;

conditionalAndExpression
    :   unaryExpression    ( '&&' conditionalAndExpression )?
    ;

unaryExpression
    :   '!' primary
    |   primary
    ;
primary
    :   inputname
        | parConditionalExpression
    ;

transition
    :   'transition' ( transition_condition )? ID ';'
    ;

transition_condition
    :   'if' '(' conditionalExpression ')'
    ;

inputdictionary
    :   'inputdictionary' '{' input* '}'
    ;

input
    :    inputname '{' description?  (inputtype)?  actionspecification? '}'
    ;


inputname
    :   (ID '.')? ID
    ;

inputtype
    :   'type' ('transient' |'durable' ) ';'
    ;


actiondictionary
    :   'actiondictionary' '{' action* '}'
    ;

action
    :   ID '{' description? actionspecification? '}'
    ;

actionspecification
    :   'send' inputname (',' inputname)* ';'

WS  :    (' '|'\r'|'\t'|'\n')+ {skip();}
    ;

ID
    :   Letter (Letter|JavaIDDigit)*
    ;

fragment
Letter
    :  '\u0024'           /* $ */ |
       '\u0041'..'\u005a' /* A-Z */ |
       '\u005f'           /* _ */ |
       '\u0061'..'\u007a' /* a-z */ |
       '\u00c0'..'\u00d6' |
       '\u00d8'..'\u00f6' |
       '\u00f8'..'\u00ff' |
       '\u0100'..'\u1fff' |
       '\u3040'..'\u318f' |
       '\u3300'..'\u337f' |
       '\u3400'..'\u3d2d' |
       '\u4e00'..'\u9fff' |
       '\uf900'..'\ufaff'
    ;

fragment
JavaIDDigit
    :  '\u0030'..'\u0039' |
       '\u0660'..'\u0669' |
       '\u06f0'..'\u06f9' |
       '\u0966'..'\u096f' |
       '\u09e6'..'\u09ef' |
       '\u0a66'..'\u0a6f' |
       '\u0ae6'..'\u0aef' |
       '\u0b66'..'\u0b6f' |
       '\u0be7'..'\u0bef' |
       '\u0c66'..'\u0c6f' |
       '\u0ce6'..'\u0cef' |
       '\u0d66'..'\u0d6f' |
       '\u0e50'..'\u0e59' |
       '\u0ed0'..'\u0ed9' |
       '\u1040'..'\u1049'
   ;

QUOTED
    :    '"' ( options {greedy=false;} : . )* '"'
    ;

// comments are ignored initially, and then extracted via the commentsBefore() method
COMMENT
    :    '/*' ( options {greedy=false;} : . )* '*/'
    ;