On this page:

Every AgE node has a lifecycle. Standard nodes usually follow simple schema based around initialization, processing computation and error handling. The state machine of a standard AgE node is shown on the image below.

There are eight states:

  1. The offline state is the state when the node has just started executing and is initializing.
  2. The initialized state is a state in which the node waits for or loads the configuration.
  3. In configured state the node is ready to run the computation. It may start it on its own or wait for an external command.
  4. A running node is performing the computation.
  5. A paused node is temporarily stopped.
  6. In stopped state the node has finished computations and may be safely terminated or restarted.
  7. When the node failed it is usually due to some exception thrown or due to some illegal transition.
  8. A terminated node has finished all work.

State machine service

A useful service for creating state-machine based components is located in the org.jage.platform.fsm package. The best example of using it is DefaultLifecycleManager. To use it, you need to provide:

  • an enumeration of states,
  • an enumeration of transitions,
  • actions to execute on transitions,
  • some additional configuration parameters.

Let's take a look at the definition of the DefaultLifecycleManager:

final StateMachineServiceBuilder<State, Event> builder = StateMachineServiceBuilder.<State, Event> create();

builder
 .states(State.class).events(Event.class)
 .startWith(State.OFFLINE)
 .terminateIn(State.TERMINATED)

 .in(State.OFFLINE)
   .on(Event.INITIALIZE).execute(new InitializationAction()).goTo(State.INITIALIZED).commit()
 .in(State.INITIALIZED)
   .on(Event.CONFIGURE).execute(new ConfigurationAction()).goTo(State.CONFIGURED).commit()
 .in(State.CONFIGURED)
   .on(Event.START_COMMAND).execute(new StartAction()).goTo(State.RUNNING).commit()
 .in(State.RUNNING)
   .on(Event.CORE_STARTING).goTo(State.RUNNING).and()
   .on(Event.PAUSE).execute(new PauseAction()).goTo(State.PAUSED).and()
   .on(Event.STOP_COMMAND).execute(new StopAction()).goTo(State.STOPPED).commit()
 .in(State.PAUSED)
   .on(Event.RESUME).execute(new ResumeAction()).goTo(State.RUNNING).commit()
 .in(State.STOPPED)
   .on(Event.CORE_STOPPED).execute(new CoreStoppedAction()).goTo(State.STOPPED).and()
   .on(Event.CLEAR).execute(new ClearAction()).goTo(State.INITIALIZED).commit()

 .inAnyState()
   .on(Event.EXIT).execute(new ExitAction()).goTo(State.TERMINATED).and()
   .on(Event.ERROR).execute(new ErrorAction()).goTo(State.FAILED).commit()

 .ifFailed()
   .fire(Event.ERROR)

 .shutdownWhenTerminated();

service = builder.build();

As you can see, to create a state-machine service a special builder is used. In the builder you declares states and events (line 4), an initial state (line 5) and terminal states (line 6). Then, you can define actions to execute when an event arrive in a particular state. To do this, you use a construct: in(state).on(event).execute(runnable).goTo(target state). To finish transitions definitions for state you use commit(). You can also provide actions to execute on a special event occurring in any state (lines 24-26). It is also important to provide an event to fire when the error is encountered. Usual erroneous situations are: an illegal transition, a thrown exception, etc. In the end, you can build a service, as shown in the last line.

To use the service you just need to translate environment changes to events using fire() method. In other words, when there is a situation triggering an event, you should pass a correct value to this method, e.g.:

service.fire(Event.PAUSE);

The service will do the rest for you.

Attachments:

Lifecycle.png (image/png)