StatefulJ Framework

The StatefulJ Framework leverages StatefulJ FSM, StatefulJ Persistence and Spring Data to seamlessly integrate StatefulJ into your Application. Using Annotations to define your State Model, StatefulJ will auto generate the FSM infrastructure, bind with the optional Endpoint Providers and manage all State Persistence.

Installation

Installing StatefulJ Framework is dependent on the technologies within your stack.
The StatefulJ Frameworks is comprised of Persisters and optional Binders .

StatefulJ provides a set of optional Binders.
Binders “bind” Endpoint Providers to the StatefulJ Framework.
The Binders are responsible for forwarding incoming requests from the Endpoint Providers as events along with the accompanying input. The StatefulJ Framework supports the following Endpoint Providers:

However, Binders aren’t required. You can invoke the StatefulJ framework directly. For information, consult Inject the StatefulFSM

Persisters interacts within the underlying databases. StatefulJ supports the following Persisters:

So, depending on your stack, you will need to include the following dependencies into your Maven project:

Binders

SpringMVC

<dependency>
	<groupId>org.statefulj.framework</groupId>
	<artifactId>statefulj-framework-binders-springmvc</artifactId>
	<version>3.0</version>
</dependency>

Jersey

<dependency>
	<groupId>org.statefulj.framework</groupId>
	<artifactId>statefulj-framework-binders-jersey</artifactId>
	<version>3.0</version>
</dependency>

Camel

<dependency>
	<groupId>org.statefulj.framework</groupId>
	<artifactId>statefulj-framework-binders-camel</artifactId>
	<version>3.0</version>
</dependency>

Persisters

JPA

<dependency>
	<groupId>org.statefulj.framework</groupId>
	<artifactId>statefulj-framework-persistence-jpa</artifactId>
	<version>3.0</version>
</dependency>

Mongo

<dependency>
	<groupId>org.statefulj.framework</groupId>
	<artifactId>statefulj-framework-persistence-mongo</artifactId>
	<version>3.0</version>
</dependency>

Coding

To integrate the StatefulJ Framework, you define your State Model. To create a State Model, you will need to:

Instantiate the Stateful Factory

The Stateful Factory will inspect the StateControllers, bind with with Endpoint Providers and construct the State Model. In order for the Stateful Factory to discover the StateControllers, you must ensure that you have correctly defined your Spring Component Scans to include the StateController’s package or individually define each StatefulController bean via Spring XML or Java Config.

XML Configuration

 	<bean id="statefulJFactory" class="org.statefulj.framework.core.StatefulFactory" />

Java Configuration

	@Bean
	public StatefulFactory statefulJFactory() {
		return new StatefulFactory();
	}

Jersey Binding

For Jersey Binding, you must initialize an instance of StatefulJResourceConfig by extending StatefulJResourceConfig and mapping a root path.

@ApplicationPath("/ajax")
public class JerseyConfig extends StatefulJResourceConfig {
	
}

Define your Stateful Entity

A Stateful Entity is a Class that is persisted, managed by Spring Data and contains a State field.
The State Field defines the current State of the Entity and is managed by StatefulJ Framework.
The state field is annotated with the @State annotation.
For your convenience, you can inherit from either the StatefulEntity Class (JPA) or StatefulDocument Class (Mongo). This will automatically define a state field annotated with the @State Annotation.

Stateful Entity (JPA)


// Stateful Entity
//
@Entity
public class Foo extends StatefulEntity {
	
	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE)
	Long id;	

	public Long getId() {
		return id;
	}
	
	public void setId(Long id) {
		this.id = id;
	}
	
}	

Stateful Document (Mongo)


// Stateful Document
//
@Document
public class Foo extends StatefulDocument {
	
	@Id
	String id;	

	public String getId() {
		return id;
	}
	
	public void setId(String id) {
		this.id = id;
	}
	
}	

Note: StatefulJ Framework determines the type of Persister to use for an Entity by the Entity’s association with a SpringData Repository. As such, you will need to define a Repository like so:

public interface FooRepository extends JpaRepository<Foo, Long> {
	
}
public interface FooRepository extends MongoRepository<Foo, String> {
	
}

Define your States

A State defines the state value for an Entity and is of type String. It is recommended that you define your States as public static constants within your Stateful Entity Class.


// Stateful Entity
//
@Entity
public class Foo extends StatefulEntity {
	
	// States
	//
	public static final String NON_EXISTENT = "Non Existent";
	public static final String ACTIVE = "Active";
	public static final String UPGRADE_PENDING = "Upgrade Pending";
	public static final String UPGRADED = "Upgraded";

	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE)
	Long id;	

	public Long getId() {
		return id;
	}
	
	public void setId(Long id) {
		this.id = id;
	}
	
}	

Define your Stateful Controller

Stateful Controller defines the set of Transitions for the Stateful Entity. It is defined by annotating the Class with the @StatefulController Annotation. The @StatefulContoller defines the Stateful Entity the Controller is managing.

field Type Description
clazz Required The Entity class managed by the StatefulController
startState Required The Starting State. When the StatefulJ framework is invoked without a Stateful Entity, then the framework will create a new instance of the Stateful Entity via the Factory bean. The StatefulJ Framework will set the state of the newly created Stateful Entity to the startState value.
value Optional The value may indicate a suggestion for a logical component name, to be turned into a Spring bean in case of an autodetected component.
stateField Optional The name of the managed State field. If blank, the Entity will be inspected for a field annotated with State Annotation
factoryId Optional The bean Id of the Factory for this Entity. The Factory Class must implement the Factory Interface. If not specified, the StatefulJ Framework will use the default Factory Implementation.
finderId Optional The bean Id of the Finder for this Entity. The Finder Class must implement the Finder Interface. If not specified, the StatefulJ Framework will use the default Finder Implementation.
persisterId Optional The bean Id of the Persister for this Entity. The Persister is responsible for updating the State field for the Stateful Entity. The Persister must implement the Persister Interface. If not specified, the StatefulJ Framework will use the default Persister Implementation.
blockingStates Optional Defines the set of “Blocking” States. A Blocking State is a State that “block” an event from being handled until the FSM transitions out of the Blocking State
noops Optional An array of NOOP Transitions. These Transitions will update the State field but will not invoke any Actions
retryAttempts Optional Specify the number of times StatefulJ should attempt to handle the event. If retryAttempts is -1, then it will always attempt to handle the event. Default is 20 retry attempts
retryInterval Optional The interval, in milliseconds, between each retry attempt. Default is 250 milliseconds between retries
@StatefulController(
	clazz=Foo.class,
	startState=NON_EXISTENT
)
public class FooController {
}

Domain Entity Support

If you are designing applications using a Domain Driven Design (DDD) architecture, then you will want to integrate StatefulJ directly into the Domain Entity or Aggregate Root. If the StatefulController is bound to an Entity, then when StatefulJ fetches/creates the Entity, it will automatically perform Dependency Injection on the Domain Entity. As such, you will not need to define your Entity as @Configurable to perform dependency injection

@StatefulController(
	clazz=Foo.class,
	startState=NON_EXISTENT
)
public class Foo {
}

Define your Events

An Event is simply a String that directs the StatefulJ Framework how to bind an Endpoint to the Framework. The format of the Event is <binder>:<event>

Binder Format Description
<event> Identifies an event that isn’t bound to an Endpoint. It is invoked directly from a StatefulFSM reference
SpringMVC springmvc:<get&verbar;post&verbar;patch&verbar;put>:<uri> SpringMVC events require an http verb and a uri. If the verb isn’t specfied, it will default to a GET. The uri must include an identifier for the Entity denoted by {id}, eg. springmvc:post:/foo/{id}/eventA
Jersey jersey:<get&verbar;post&verbar;patch&verbar;put>:<uri> Jersey events require an http verb and a uri. If the verb isn’t specfied, it will default to a GET. The uri must include an identifier for the Entity denoted by {id}, eg. jersey:post:/foo/{id}/eventA
Camel camel:<route> Camel events map to a route. Since routes are typically not resource oriented, you will have to annotate a field in the Message with an @Id annotation indicating the ID of the Entity or declare a field named id
@StatefulController(
	clazz=Foo.class,
	startState=NON_EXISTENT
)
public class FooController {

	// Events
	//
	public static final String CREATE_FOO = "springmvc:post:/foo";
	public static final String GET_FOO = "springmvc:get:/foo/{id}";
	public static final String UPGRADE_REQUEST = "springmvc:post:/foo/{id}/upgrade";
	                                             "springmvc:post:/foo/{id}/upgrade";
	public static final String UPGRADE_APPROVED = "upgrade.approved";
	public static final String SUBSCRIPTION_REVOKED = "camel:vm:subscription.revoked";
	

}

Define your Transitions

A Transition is a reaction to an Event directed at a Stateful Entity. The Transition can involve a possible change in State and a possible Action.

In the StatefulJ Framework, a Transition is a method in the Stateful Controller annotated with the @Transition annotation.

Field Value Description
from <state> or &ast; The “from” State. If left blank or the state is “&ast;”, then this transition applies to all states
event <event> A String that defines the Event
to <state> or &ast; The “to” State. If left blank or the state is “&ast;”, then there is no change from the current state
reload true|false If true, StatefulJ will reload the Entity before invoking the Action method. Default is false

When a Transition is invoked, the StatefulJ Framework will call the associated method.
If the StatefulController is a Controller (vs a Domain Entity), then the first two parameters of the method are always:

  1. Stateful Entity
  2. The Event

Note: When the StatefulJ Framework binds the Endpoint, it reads all the Annotations on the method and all the Parameters after the StatefulEntity and Event and then propagates these parameters to the Endpoint. So, you can define your Transition with the parameters and annotations that you normally would for the Endpoint.

@StatefulController(
	clazz=Foo.class,
	startState=NON_EXISTENT,
	noops={
		@Transition(from=UPGRADE_PENDING, event=UPGRADE_APPROVED, to=UPGRADED)
	}
)
public class FooController {

	// Events
	//
	public static final String CREATE_FOO = "springmvc:post:/foo";
	public static final String GET_FOO = "springmvc:get:/foo/{id}";
	public static final String UPGRADE_REQUEST = "springmvc:post:/foo/{id}/upgrade";
	public static final String UPGRADE_APPROVED = "upgrade.approved";

	@Transitions({
		@Transition(from=NON_EXISTENT, event=CREATE_FOO, to=ACTIVE),
		@Transition(from=ACTIVE, event=UPGRADE_REQUEST, to=UPGRADE_PENDING),
		@Transition(event=GET_FOO)
	})
	public String details(Foo foo, String event, Model model) {
		model.addAttribute("foo", foo);
		return "foo-details";
	}
}

If the StatefulController is a Domain Entity, then the first parameter is:

  1. The Event
@StatefulController(
	clazz=Foo.class,
	startState=NON_EXISTENT
)
public class Foo {

	// Events
	//
	public static final String UPGRADE_APPROVED = "upgrade.approved";
	
	@FSM
	StatefulFSM<Foo> fsm;
	
	public approveUpgrade() {
		fsm.onEvent(UPGRADE_APPROVED);
	}
	
	@Transition(from=UPGRADE_PENDING, event=UPGRADE_APPROVED, to=UPGRADED)
	private void doNotifyOfUpgrade(String event) {
		FooObserver.notifyUpgrade(new UpgradeEvent(this));
	}
}

Propagating Events and Non-Determinism

If your method returns a String, and that String is prefixed with event:, then the return value will be treated as an Event and re-propagated. This can be used to implement Non-Deterministic Transitions. For example, you can define a Transition without a to and in the associated method do further conditionals to determine an event to propagate. The returned event can then determine the actual State change.


	@Transition(from=STATE_A, event=EVENT_A)
	private String doNonDeterministic(Foo foo, String event) {
		if (foo.getBar().equals("b")) {
			return "event:" + EVENT_B;
		} else {
			return "event:" + EVENT_C;
		}
	}

	@Transitions({
		@Transition(from=STATE_A, event=EVENT_B, to=STATE_B),
		@Transition(from=STATE_A, event=EVENT_C, to=STATE_C)
	})
	private void doDeterministic(Foo foo, String event) {
		System.out.println("Event = " + event);
		System.out.println("State = " + foo.getState());
	}

Inject the StatefulFSM

The StatefulJ Framework provides the ability to route Events directly to the FSM. This is done by invoking the onEvent method of the associated StatefulFSM class. To obtain a reference to the StatefulFSM, you annotate a reference with the @FSM annotation. The StatefulJ Framewok will automatically inject the StatefulFSM based off the declared Generic Type. If there is more than one StatefulController that qualifies, then you can provide the bean Id of the specific StatefulContoller.

Field Description
value The Id of the Stateful Controller. If not specified, it will determine the FSM based off the Type

@FSM
StatefulFSM<Foo> fsm;

public void upgradeApproved(Foo foo) {
	fsm.onEvent(foo, UPGRADE_APPROVED);
}