Level: Intermediate James Carey (jecarey@us.ibm.com), Senior Software Engineer, IBM Geoffrey Beers (gbeers@us.ibm.com), Software Engineer, IBM
21 Feb 2007 In the first article of this series, we discussed the differences between
BPEL processes and Business State Machines, observing that state machines provide a
good solution for "nouns" that are event driven and have looping. We created
a basic vending machine and tested it using the BPC Explorer. This article further enables your
BSM skills, so you can enhance the vending machine to collect money, introduce actions,
and test using component test. In the final article, you will extend the vending machine by making it dispense products,
introducing conditions and timeouts, and testing it using the debugger. You'll also look
at simplifying the state machine through the use of composite states.
Introduction
The Business State Machine (BSM) component in IBM® WebSphere®
Process Server (hereafter, referred to as Process Server) provides a way to define and work
with business "nouns".
These nouns are things like "orders" or "trips" that have a lifecycle controlling their behavior.
For example, the cancel operation on a trip will not be supported once the trip is completed, and
will further need to process things differently for a trip that has been reserved versus one that has not.
Business State Machine provides a way of developing, debugging, and monitoring these nouns as a
state machine.
This series of articles provides an overview of Business State Machines,
including an overview of its capabilities and a discussion of using BPEL processes as an alternative.
We do this by building a vending machine. In each article, we enhance the vending machine by using
more of the BSM capabilities. In the first article we examined some fundamental features of BSM, and
showed how these concepts can be used to implement the most basic vending machine. We then
tested it using the BPC Explorer. In this second article, we will make our vending machine collect
money, introduce actions, and test it using component test. In the final article, we will extend the
vending machine by making it dispense products, introducing conditions and timeouts, and
testing it using the debugger. Finally, we will look at simplifying the state machine through the
use of composite states.
Powering on and off
In the last article, we create and tested the vending machine maintenance interface.
This interface controls powering the vending machine on and off, shown in Figure 1.
Figure 1. The simple vending machine with power
on and power off
Collecting money
Now that we can turn the vending machine on and off, it needs to be able to collect money.
We do this by adding a new state to the state machine called Collecting, as well
as two new operations to the state machine's interface: deposit
and coinReturn. We want to keep the maintenance interface separate
from the operational interface, so that we have more control over the exposure of the interface.
For example, we do not want to export the maintenance interface on the module containing the
vending machine, but still want to expose the operational interface. Thus, we create a new interface
in the interface editor, shown in Figure 2.
Figure 2. Interface operations to deposit and return
money
The deposit operation is called when money is deposited into the vending machine.
The amount deposited is passed as depositAmount, which is the value in cents.
The coinReturn operation is used to return the money put into the machine.
The modified state machine is shown in Figure 3.
Figure 3. Vending machine with added Collecting state
Now when the vending machine is in the Running state, it can accept either
the powerOff or the deposit operation.
The deposit operation causes the transition to
the Collecting state. Once we are collecting money, we need to keep track of
how much money has been deposited. As shown in Figure 4, to do this we add the variable totalDeposited
to the BSM.
Figure 4. totalDeposited variable added to state
machine
However, merely adding the variable is not enough. We also have to write some Java™ code that will
update the value as we move through the state machine. This is done by adding actions to the state machine.
One type of action is the transition action. Transition actions only occur if the transition is taken.
These actions are identified by the blue pinwheel, such as Update Total
on the transition we were just discussing. These actions can be either a Java snippet or an invocation of
another component. Right now we'll focus on Java snippets and return to invokes later.
For actions associated with a transition caused by an operation, the input and output parameters
are available as local variables. The pattern is <operation>_Input_<parameter>
and <operation>_Output_<parameter>. For example, in the case of the actions associated
with the transition caused by the deposit operation, the variables deposit_Input_serialNumber
and deposit_Input_depositAmount are available. Note that our examples do not have an output variable,
since these are only available when two-way operations are defined in the interface.
In the vending machine example, the following transition actions can occur:
- Initialize
- This action does any initialization needed for the vending machine. In this example we
set the
serialNumber of the state machine into a variable, which
allows us to use the serial number when we print messages to the system log.
- Update Total
- These actions add the deposited amount to the
totalDeposited variable.
- Return Money
- This action would do whatever processing is needed to return the deposited money.
This is just a placeholder in this example.
- Shutting down
- This action would do whatever shutdown activity is needed. In this example, this is also just a placeholder.
For this example, we have also added code in each of these actions to print messages to the system log.
The other types of actions are the state entry and exit actions. These are actions that occur when a state is
entered (or exited) regardless of which transition caused the state to be entered (exited). These are shown
in the bottom part of the state and are identified with an arrow going into it (entry) or out of it (exit). In our example,
the Running state has an entry action called Zero Total.
This action sets the value of totalDeposited to zero whenever the
Running state is entered. In our example, Running could be entered either
because of the powerOn operation causing a BSM to be created,
or from a coinReturn operation when in the Collecting
state.
There are a few special cases for entry and exit actions. Because the action on the transition coming out of the
intial state provides equivalent support, initial states do not support either. Final and terminate states support
only entry actions, since these states are never exited. Entry actions are one reason that multiple final (or terminate)
states would be specified for a state machine. For example, a state machine may have a final state that is reached
as part of normal processing that doesn't have an entry action and a second final state that is reached due
to the state machine being cancelled. This second final state could have an entry action that logs information
about the cancellation.
Let's go back to the new transitions in the vending machine. We discussed the transition caused
by the deposit operation when the BSM is in the Running
state; however, the deposit operation is also valid when in
the Collecting state. This type of transition (where the source and target state
are the same) is called a self-transition. There are two types of self-transitions, and the difference is important
to how the state machine behaves. An internal self-transition is one where the state is never logically left, while
an external self-transition logically leaves and returns to the same state. In our current example, it does not matter
which self-transition method we use. However, if we had an entry (or exit) action on
the Collecting state this would be very important. For an internal self-transition,
entry and exit actions are not executed when the self-transition is taken; the state is never entered or exited,
so these shouldn't be run. On the other hand, for an external self-transition entry and exit actions would
be taken, since the state is left and subsequently returned to. There are other differences which we will discuss
later.
The last new transition is the one from the Collecting state caused by
the coinReturn operation. The addition of this transition creates a loop
between the Running state and the Collecting state;
the vending machine can transition an indeterminate number of times between these two states.
Getting the vending machine state
Often the user of the BSM will want to know the state machine's state.
For example we want to be able to find out if the vending machine is in
the Running state or in the Collecting
state. In order to do this, BSM supports the definition of two special operations
called getState and getDisplayState.
When one or both of these operations are defined, BSM will generate their implementation.
For the getState method, the implementation will return
the internal state name that the state machine is currently in. We make the distinction
that this returns the internal state name,
because this is not the state you see in the BSM editor. For example, the internal state name
for Collecting is State2.
Unfortunately there isn't anyway in the BSM editor to see this. In order to find out these
names you have to look at the SACL file as XML. To do this, close the BSM editor for the state
machine, right-click on the BSM, select Open With > Text Editor.
Figure 5. View state machine in text editor to get state
name
Note that this selection sticks, so you will need to close the SACL file and select Open With >
Business State Machine Editor to open it in the editor again.
Looking at the SACL file there is entry:
<sacl:state displayName="Collecting" name="State2"/>
This defines a simple state which internally is called State2 but will
be called Collecting when it is displayed. On the other hand, the
implementation for getDisplayState returns the displayName instead of the
internal name.
For either operation you should note that a state machine is never in the initial state (since it always immediately
transitions) and is destroyed when it enters the final state, so these states will never be returned when calling
these operations. In fact, calling this operation on a state machine that has reached its final state will return an
exception. You should also note that these operations return the persistent state of the BSM and not the the
dynamic state of the BSM. In other words, if the BSM has transistioned from Running to Collecting, but has not
completed processing, the values for Running (State1) will be returned.
In order to have BSM generate support for the getState method, the
operation must be declared in an interface. In the vending machine example, we added it to the
maintenance interface, as shown in Figure 6.
Figure 6. getState
operation added to interface
Notice that getState is a two-way operation. The input parameters are whatever
is needed for correlation and the output must be a single parameter called state of type string.
Specification of getDisplayState is the same except for the operation name.
Now when the vending machine is generated it will support the getState operation.
Getting the vending machine state
In the previous article, we tested the vending machine using the BPC Explorer. In this article we'll test it
using Component Test. To use component test go to the wiring diagram, right-click on the vending machine
component, and select Test Component. This brings up the screen shown in Figure 7, which will allow
us to call operations on the vending machine.
Figure 7. Test the vending machine with component test
This article will not go into details on component test, since we are focusing on state machine testing.
The first thing we need to do is to create the state machine. This is done
via the powerOn operation on the maintenance interface.
Thus we need to change the interface and the operation using the drop downs shown in Figure 8.
Figure 8. Select the powerOn operation in component test
Component test will automatically fill in the parameters (and their types) for the selected operation.
To execute the operation, simply fill in the serial number (123 in this case) and click the Continue button.
You will be asked to select a server, which will be started automatically, if it is not already started. The operation
will be invoked and this will appear in the events window, shown in Figure 9.
Figure 9. Starting a vending machine instance in component test
Also the logs in our actions will show up in the console:
Figure 10. Vending machine snippet logs
To call another operation, press the invoke icon in the upper right portion of the Events window, select the
operation, fill in the data parameters, and click the continue button. Let's make our first deposit to the
vending machine:
Figure 11. Deposit money in the vending machine
The result is that in the Events window:
Figure 12. Results in component test after deposit
And in the console we see:
Figure 13. Results in snippet logs after deposit
We can also call getState from component test:
Figure 14. Calling getState
in component test
Since getState is a two-way operation, it has a response. This shows up as a
Return element in the Events section:
Figure 15. Return event from getState
The Return element is immediately displayed in the properties section and shows the state returned
by getState:
Figure 16. Returned value from getState
As described above, State2 is the internal state name for the Collecting state.
If we want to execute the same operation with the same parameter values, we can right-click an Invoke in the
Events window and select Rerun. This is especially useful for executing the getState
operation, since the input parameter doesn't change.
BPC Explorer versus component test
The BPC Explorer is always available, even in a production system, so it can always be used to work with a BSM.
It does, however, have a major shortcoming. Currently when calling an operation on a BSM, the return parameter
cannot be displayed. This means that the getState operation is of no value in that
environment. This will be addressed in future releases, but for now, if the return values are needed, then the
operation would need to be called via some other mechanism, such as a JSP or, if testing, via component test.
The major advantage of component test is that calls from the BSM can be easily monitored and emulated.
For example, if the Return Money action called out to another component, we could test that the call is correctly
formed and that the vending machine, could handle the response correctly, if a response existed. We will return
to this in a later article.
Summary
In this article, we extended the vending machine to include the collecting money. This provided our first
self-transition, the addition of variables, and actions. We then used the component test to test the updated
vending machine. In the upcoming article, we will extend the vending machine by making it despense
products and we will look at how to monitoring and debugging BSMs.
Resources Learn
Get products and technologies
- Build your
next development project with IBM trial software, available for download directly from
developerWorks.
Discuss
-
Check out Bobby Wolf's WebSphere SOA and J2EE in Practice blog. This blog discusses how to use SOA, J2EE, and related technologies to develop business applications, including how to make best use of IBM J2EE products like WebSphere Application Server and Rational Application Developer, and IBM SOA products like WebSphere Process Server and WebSphere Integration Developer.
About the authors  | |  | James Carey is currently a senior software engineer with IBM's System & Technology Group. Previously James was a member of the WebSphere Process Server architecture team where he focused on Business State Machines. |
 | 
|  | Geoffrey Beers is a software engineer at IBM in Rochester, Minnesota. He works in the Bringup Lab and SWAT team for WebSphere Process Server. |
Rate this page
|