Pseudo-Transaction Management with OpenEmcee Microflows (Beta - Unedited)
By Scott Schenkein
Software systems often have complicated error-handling requirements.
Implementation of these requirements can often lead to code that is difficult
to understand and maintain. The OpenEmcee Microflows "undo" mechanism
is designed to provide a consistent way to handle "pseudo-rollback" of
previously completed work when an error occurs.
A common solution to this issue is a simple database rollback. As some
systems are not purely implemented using database (or other XA compliant)
transactions, special work must be done to back-out changes made during
business processing.
The Business Case
Consider a simple airline reservation system with the following requirements.
When booking a flight, the system must:
1. Reserve the flight with the airline.
*If any part of this process fails, this reservation must be
backed out.
2. Update the customer's profile to denote that they reserved this
flight.
* If any part of the process fails, the customers account must
be returned to its previous state.
3. Authorize the flight amount on the customer's credit card.
4. Update the reservation database with the credit authorization
information.
|
Solutions, solutions…
This problem could be approached in several ways, for example:
- Manually keep track of what has happened, and then write code to undo
work at every possible failure point. This might become broken when
new requirements are introduced into the system.
- Create transactional EJB's which use the javax.ejb.SessionSynchronization
interface to detect a rollback, and implement the custom rollback logic
for the non-database error-handling requirements (see requirement 1,
2 would be handled by normal database rollback). Consider that this
solution couples the error handling with the core processing.
- Use OpenEmcee "undos". (12/12/2003) Note that an EJB implementation,
which integrates OpenEmcee Microflows and its "Undo" functionality into
Java enterprise-transactions will soon be available.
Lets Choose Step 3 (I bet you didn't see that coming)
An OpenEmcee Microflow task definition can include two different tasks.
One is the business task, and one is the undo. Undos are optional and
business tasks are required.
All undos (for tasks which successfully executed) will be invoked if
the Microflow terminates with a failure (Additionally when the EJB
implementation is available, the undos will be called if the EJB transaction
rolls back.):
<task name=…
<outcome name="whatever" terminal="true"
failure="true"/>
</task>
|
The implementation of an undo task is identical to that of a business
task. Simply create a class which implements the "org.openemcee.microflow.usercore.MicroflowTask"
interface, implementing your back-out (undo) logic in the "executeTask"
method. For a review of how to implement a MicroflowTask, please see the
OpenEmcee Intro.
Associating an Undo with a Task
Please see the following job-descriptor snippet for an example of how
we associate the undo. Inline comments should provide commentary.
<!--
Begin the Microflow by Reserving the ticket. -->
<first-task>ReserveTicket</first-task>
<!-- Reserve the ticket, if the flow fails,
the UnreserveTicket task will be called.
-->
<task name="ReserveTicket"
jclass="com.airline.ReserveTicketTask"
undoJclass="com.airline.UnreserveTicketTask">
<outcome name="success"
nextTask="UpdateCustomerProfile"/>
<outcome name="failure"
terminal=”true”
failure=”true”/> <!-- Failure is true, terminate, and
call any pending “undos”. -->
</task>
<!-- Update the customer profile, if the flow fails,
the RestoreCustomerProfile task will be called.
-->
<task name="UpdateCustomerProfile"
jclass="com.airline.UpdateCustomerProfile"
undoJclass="com.airline.RestoreCustomerProfile">
<outcome name="success"
nextTask="ProcessCredit"/>
<outcome name="failure"
terminal=”true”
failure=”true”/>
</task>
<task name="ProcessCredit"
jclass="com.airline.ProcessCredit”>
<outcome name="success"
nextTask="UpdateReservationDB"/>
<outcome name="failure"
terminal=”true”
failure=”true”/>
</task>
<task name="UpdateReservationDB"
jclass="com.airline.UpdateReservationDatabase”>
<outcome name="success"
terminal=”true”
failure=”false”/> <!--False
is the default value -->
<outcome name="failure"
terminal=”true”
failure=”true”/>
</task>
|
Some Sample Executions
1. In the case where all steps complete successfully, none of the undos
will execute.
2. In a case where "ReserveTicket"
succeeds, but "UpdateCustomerProfile"
fails
- The "UnreserveTicketTask"
is executed.
- "RestoreCustomerProfileTask"
is not called because "UpdateCustomerProfile"
never successfully completed.
3. In a case where all steps complete successfully except for the final
"update reservations DB":
- The "RestoreCustomerProfileTask"
is executed.
- The "UnreserveTicketTask"
is executed.
- Note that the undos are executed in the opposite order to which their
respective business tasks were executed. (Undo's are stored in a stack).
Some Limitations to Note
At the time of this documents writing (12/12/2003), the following limitations
effect the "undo" facility.
- There is no facility for handling failures of Microflow undo tasks.
- There is no facility for passing parameters to Microflow undo tasks.
If the OpenEmcee Microflow community expresses an interest in this
functionality, it will be added to the system.
If the OpenEmcee Microflow community expresses an interest in this functionality,
it will be added to the system.
What we have learned
In this installment, we have learned how to separate complex error handling
from core business processing. This facility can prove useful in creating
systems with complex error-handling requirements that are easily understandable
and maintainable.
|