Table of contents
- A short example
- State machine
- Transitions and validations
The jStates library helps developers to design and easily implement and use simple state machines. The machines created using this library are intended for general use, not restricted to a certain framework or Java platform (J2SE, J2EE).
This library version requires J2SE 1.4.2 or above.
This documentation shortly describes the jStates library and API. See the jstates-utils and jstates-examples sourceforge packages documentation and javadoc for more information about Jakarta Commons Validator integration and library usage examples.
State machines are used in many scientific and technological fields: mathematics, computer science, engineering and many more.
There are several types of state machines, each one suited for different uses. This library does *not* offer a complete implementation of any of them, but a simplified and generic approach to some of them. This simplified approach makes learning jStates easy. Using and integrating jStates machines is easier than using other more specific state machines. You can use jStates to easily define application flow, GUI (desktop and HTML) complex form navigation, form data validation, serialize state machines, generate localized error messages, use your own validators or scripting languages to check state values and more.
A jStates state machine can be divided into the following parts:
- The machine itself, has a name that identifies it and a XML file that defines it. Its main functions are to act as a container of states and to act as an interface between the developer and these states. A machine is always in a well defined state ("current state"). A machine can change its current state by "forwarding" to another state through a "transition".
- A state is a set of properties and transitions. Properties store the current value of a state, while transitions show what other states can be reached from a state.
- A transition is a little more complex concept. Think of it as a condition check to change the current state of a machine. A transition contains a set of validator rules that state properties must conform, for a machine to be able to reach another state from the current one. When we ask a machine to follow a transition ("forward"), the machine tests all of the validator rules of the transition and, if all of the tests are correct, the machine changes its current state to the one pointed by the transition. Transitions of a state can only be followed if that state is the current one.
- A validator rule is a set of conditions that a state machine must verify before a transition can be followed. A validator rule can be written as a custom class ("validator") or a script ("script"). More on this in the following sections.
To create and use a jStates state machine, we should determine the possible states of a machine, determine the transitions from each state to the other states and determine what validator rules must conform a state to allow transitions to another states. The following sections describe these steps in more detail.
A short example
This section shows an example of how to use jStates. The presented example is just a couple of commented code blocks, for a more detailed explanation of this code, please refer to the next sections.
Machine definition files are XML files where a developer can define a state machine and all of its elements: states, transitions, validators and so on. Currently, the DTD for XML validation of machine definition files is statemachine_0_9.dtd. Take a look at this DTD file for detailed documentation about XML tags and attributes. This is a simple but complete example of a XML definition file, mytestmachine.xml: *** TODO I DON'T LIKE THIS CODE
After machine definition comes machine use. This is a Java source code snippet, demonstrating how to create and use the previously defined machine:
See jstates-examples package in sourceforge.net package downloads for more Java source code and XML machine definition examples.
As previously said in the "Introduction" section, a state machine is both a states container and an interface to interact with these states. The first step to use a machine is to define it. The second should be to use Java code to instantiate it and use it.
The XML tag to define a machine is the
<statemachine> tag. This tag has three attributes:
name: contains the unique name of the machine. It's used for machine instance creation, more on this later in this section.
initialStateName: contains the name of the initial state, the first "current state" of the machine just created.
info: contains extra information about the machine, think of this as a place to include metainformation - a descriptive test of machine usage, a URL, version information, ...
Once defined, we should load the machine definition into the
StateMachineFactory, using the method
addMachineDefinition. Once loaded, we can create instances by calling the
createMachine method, with the machine
name as argument. Any machine instance is independent of the other instances, so we can create any number of machines of the same type:
These are the most common operations that we will perform on a machine:
Examinate the current state: call the
getCurrentStatemethod. It will return a
Stateobject. More on
Stateobjects in the next section.
Change the current state to another: call the
forwardoverloaded method, see the
StateMachinejavadoc for more information about these methods. These methods return a boolean value, indicating whether the machine changed its current state ("true") or not ("false"). If the state change failed, there could be stored error messages about the validation that aborted this change. Call the
getTransitionErrorsmethod to obtain these errors. More on this in the "Transitions" section.
Change the internal value of the current state: we can do this in two ways. First way is to get the current state by calling
getCurrentStateand then calling the
setmethod on this state (more on this in the next section). Second way is by calling the
updateCurrentStatemethod, and passing a JavaBean object that has properties with the same name as the state properties. The properties with the same name will be copied to the current state and the rest will be ignored. This last method is useful if you want to populate the current state with form field values contained in a bean, for example.
StateMachine object can perform other interesting operations, including going back to previous states, serialization and more. See the javadoc documentation for more about this.
State objects represent the status of a
StateMachine object and the possible states a machine can reach from its current state. To accomplish these goals, a
State contains two types of objects:
Properties: they have the same functionality as JavaBean properties or classic variables, storing the internal value of a state. In a machine definition XML, a property can have a default value or no value (empty String), asigned by the
valueattribute in the
<property>tag. However, this value can be changed by calling the
setmethod of a
Stateobject or by calling the
StateMachine. In the same way, property values can be read by using the get methods
Stateobject. Internally, properties are stored as String objects.
A machine can be in a well defined state ("current state") and its status will not change if the internal properties of the current state do change their values. Properties are used to decide if its possible to change to another state from the current one, so they are related to transitions, not to the machine status. See the "form validation" (TODO: IMPLEMENT) example in the jstates-examples package to see a practical use of this feature.
Transitions: there is a transition for each state that can be reached from the state this transition is contained into. When a machine tries to change its current state by calling "forward" methods, it must call a transition of the current state to do so. See the next section, "Transitions", for a more complete explanation about this process.
There can be any number of transitions contained into a
State, even none of them. If a state has no transitions, it is called a "final" state.
A XML example:
And this is a Java code snippet, demonstrating state and property handling:
As previously mentioned, a state machine can be only in a well-known state, that we called the "current state". When first created, the machine sets its current state to the state referenced by the
initialStateName attribute of the
<statemachine> tag. From there, a machine is supposed to change its current state to another state when some significative event happens, like changing from a form web page to another form page, for example.
Transitions are the way to change the current state. Each state contains zero or more transitions, each one pointing to a destination state. When a machine wishes to change its current state, it "talks" to its current state and asks it for a transition to the destination state. From this point on, two things can happen: the current state properties have correct values and the transition is successful, or the current state properties do not have correct values and the transition fails. If the transition is successful, the current state of the machine changes to the destination state; if not, the machine's current state does not change.
So, a successful transition really depends on two conditions: state property values and "correctness" of their values.
How to check that values are correct? The answer is by using "validator rules". A validator rule is an executable code
that gets a
State object as argument (always the current state) and returns a boolean "true" or "false" after verification
of certain conditions about that state's parameters values.
This is a diagram of a transition with a validation rule:
An this is the XML code for the previous diagram:
The following source code demonstrates what happens in both the diagram cases, (A) and (B), using a different machine for each case:
This example demonstrates the use of a validator rule to check state property conditions. This is a script validator rule, but there is a "validator" type rule too (more on this later).
Instead of using just one simple rule, we could have used a more complicated one
or many of them to perform the transition check. If we used more than one rule, the machine
would have executed each of them, no matter if some or all of them failed. This "rule execution"
process is called "validation". All of the validator rules must be contained into a
There are two types of validator rules, "script" rules and "validator" rules. They are called "Scripts" and "Validators" for short. Besides returning "true" or "false", both rules can store text messages into an "errors" object, so these messages can be recovered after a failed validation and shown to the final user in a GUI.
The entry point for the rule is the "validate" function, though you can define another functions that get called from this one. There is an implicit "errors" object too, placed into the script global scope, that allows you to place error messages related to failed validation checks. This errors object has the type
TransitionErrors, see the javadoc for usage instructions.
For more complex validations, where a script is cumbersome to use or we need a better Java integration, the best option is to use "validators". A validator is a Java class that implements the
net.sf.jstates.validation.StateValidatorinterface and has a constructor with just a String argument, see the javadoc for details.
To include a validator into the validation process, we must include a
<validator>tag at the same level as the scripts tags, into the
These are the attributes of the
type: Contains the fully qualified class name of the class implementing the
StateValidatorinterface. This class must be in the class path for jStates to be able to instantiate it.
- parameters: This is a String containing whatever information needs the validator class for initialization. This String is passed to the validator constructor during instantiation as its only argument.
Validators can be used as bindings or connectors to another validation frameworks, such as Jakarta Commons Validator. Currently, there is a connector to Commons Validator, see jstates-utils sourceforge package for usage and integration instructions.
- type: Contains the fully qualified class name of the class implementing the