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.
Previous page Next page