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).

Previous page Next page