Taking Service-Oriented Architectures Mobile, Part 2: Mobile Transactions
| |||||||
In this series of articles, we are looking at how it is possible to extend the basic principles of service-oriented architecture (SOA) to devices at the edge of the network (such as cell phones, PDAs and set-top boxes) so that they can connect to enterprise-level services within a network.
In "Part 1: Thinking Mobile," we described how interfaces to network services could be defined and implemented at a level of abstraction, regardless of the mobile network or underlying network protocol layers. This means that we can "talk" to network services directly from any cellular phone that has a standard Java implementation (Java ME) without any concern over whether we are using 2G, 2.5G, or 3G mobile networks, or whether the underlying network protocol is iMode, WAP, or HTTP.
In this article, we are going to extend the facilities of our architecture by adding the ability for devices to become involved in atomic transactions. Transactions are vital in ensuring that all of the relevant resources that a system provides are coordinated and managed so that the overall state of the system remains consistent.
The benefits of using transactions can range from the simple, such as making sure that your seat at the movie theater is not double booked, to the essential, such as making sure that the correct piece of medical equipment can be provided to the correct field hospital in a disaster zone.
For the architecture we are designing, we need transactions to be both distributed and mobile--which, in simple terms, means that a transaction represents a "unit of work" between a set of cooperating services on the network and over the air. Under a transaction, either:
In a SOA, a transaction manager is usually a service that is used to coordinate the other services participating in the transaction. It does this by creating the transaction context and then coordinating the participating services. Normally, this is implemented using a two-phase commit model in which a commit is performed on all of the participants, or an abort is performed on all of the participants, giving us the "all or nothing" behavior that we are looking for.
If we are going to implement services such as mobile banking or logistics and supply chain applications, then we need to devise a method of making our mobile devices capable of being involved in any transaction, either explicitly by talking directly to the transaction manager, or implicitly through calls to services that are themselves transactional. Or, for maximum flexibility, both.
Our mobile SOA has targeted the lowest common denominator for Java on devices, namely MIDP/CDLC, which means that any more powerful Java-enabled device can also take advantage of the architecture. When we took at look at designing in the support for mobile transactions, we settled on two sets of interfaces that are used on the device to create, abort, and commit transactions, and also to participate in transactions.
We also needed to implement code to manage leases on transactions to set time limits on operations. This means that if parts of the system fail, we can abort the transaction after a predetermined amount of time. For example, if a mobile client becomes disconnected when your train goes through a tunnel and the network connection cannot be maintained, the transaction manager will abort the transaction when the lease expires.
If the transaction is aborted, all of the active participants for that transaction will be notified. As a result your business services can release any resources that had been "locked" for the expired transaction. To recap: mobile clients can either be explicitly or implicitly involved in any transaction.
For a simple example, our local movie theater has given us a brief to design a prototype mobile seat booking application, so that moviegoers can book a ticket to their favorite movie and reserve a seat on the way to the theater. This type of system inherently requires transactions to make sure that seats are not double booked, and that should a booking fail, the customer is not charged.
On the device, we access the transaction manager in order to create a transaction, and then use that transaction object to pass the business services participating in the transaction.
//MIDP code - using explicit transactions
//Obtain proxies for the Transaction Manager & the business service
TransactionManager txnMgr= TransactionManagerFactory.getService();
SeatReserver seatReserver = SeatReserverFactory.getService();
//creating a transaction with a five minute lease
long lease= 60 * 1000 * 5;
Transaction txn=txnMgr.create(lease);
//register the transaction with the business service
seatReserver.newTransaction(userDetails,txn);
//user has selected the date, the movie and the seat(s)
seatReserver.reserve(dateTime, movieID, seatID ,txn);
//when all operations are complete, prompt the user to commit the transaction
txnMgr.commit(txn);
In the code example above, both the TransactionManager
and SeatReserver are "shadow" proxies (to remote
services) created with the mobile stub generator, which takes a
service interface as an input and generates the code required for a
mobile client to communicate with a remote service. This is
described in detail
in part
one of this series.
Having obtained the proxies to the two services, the code then
obtains a Transaction object from
the TransactionManager with a lease of five minutes,
meaning that the client code has to complete all operations using
the transaction within that timeframe; otherwise, the transaction
manager will abort the transaction.
The transaction object is then passed to
the seatReserver along with user's details, to allow the
service to register as a participant in the transaction. More about
that later.
In this example, the SeatReserver service forms part of
the remote SOA that provides access to a billing service, which in
turn participates with the transaction created on the mobile
client.
An alternative approach would have been to encapsulate the usage of a transaction within the business service itself and have a method on the service to commit the transaction internally; for example, a "purchase" method.
In the above example, the mobile client code is responsible for committing or aborting the transaction. Let's now take a look at how your business services gets notified.
The architecture we have designed provides a simple model that allows you to connect adapters to your existing business services. This means that you do not need to make any modifications to those services, but just simply write the connector code--this gets loaded at runtime by the gateway.
In our design, the transaction manager is only responsible for
managing the two-phase commit protocol, so your business service (or
connector code) needs to implement
the TransactionParticipant interface to be able
register as a participant with
the TransactionManager.
In the earlier client example, the mobile application first created
a transaction and then passed that transaction to
the SeatReserver service, by invoking the
method seatReserver.newTransaction(). Let's put all
the pieces together and take a look at how
the SeatReserver service actually interacts with the
transaction manager.
//Service code example to participate in a Transaction public class SeatReserverImpl extends AbstractTransactionParticipant{ private TransactionManager transactionManager; public SeatReserver(TransactionManager txnMgr){ transactionManager=txnMgr; } public boolean prepare(Transaction txn){ //return true if this participant to ready to commit } public void commit(Transaction txn){ //code to actually commit the transaction } public void abort(Transaction txn){ //The transaction has been aborted } //methods invoked from mobile client public void newTransaction(UserDetails user,Transaction txn){ //once you have joined the transaction the TransactionManager will //be able to invoke methods on the TransactionParticipant methods, //commit, prepare and abort transactionManager.join( txn, this/*TransactionParticipant*/); } public void reserve(Date dateTime,long movieID,long seatID,Transaction txn){ //store data keyed on the transaction's ID //later this data is either committed to a database //or aborted depending on the final state of the transaction } }
The diagram in Figure 1 below shows the relationships between the
transaction manager and the SeatReserverImpl class
shown above.

Figure 1. Transactions UML class diagram
Looking at the UML diagram in Figure 1, you will notice that
both TransactionManager
and TransactionParticipant directly implement the Java
RMI interface
Remote, whereas the SeatReserver indirectly
implements Remote by extending
the AbstractTransactionParticipant. This is because
the TransactionManager runs as Java RMI service as part
of the overall SOA, which allows for a mixture of RMI, Jini, and
user-defined connectors that can be exposed as services.
Here is a look at the whole architecture from the mobile client's perspective:

Figure 2. Mobile transaction architecture
In Figure 2 above, you can see that the mobile device is using a "transaction pack." This is a class library containing prebuilt stubs that can communicate with the remote transaction manager. The transaction manager is loaded as a connector service by the gateway, thus making it available (via an RMI lookup) to other business services running on the network.
Transactions are vital to ensure that all of the relevant resources that a system provides are coordinated and managed so that the overall state of the system remains consistent. A transaction represents a "unit of work" between a set of cooperating services on the network and over the air, which is either executed or aborted as a whole. Transaction participants register an interest in a transaction with a transaction manager. The transaction manager is responsible for coordinating transaction participants using the two-phase commit protocol.
In a SOA where services are inherently distributed and decoupled, client applications may need to be able to interact with multiple services, all within a single transactional unit. Leases are used to ensure that transactions can be automatically aborted if the client crashes, or fails to commit or abort the transaction. Simple transaction-based systems can be implemented completely by the remote services when these services are tightly coupled. The choice of whether the client application or a remote service creates and commits/aborts a transaction is determined by the required functionality of the system you are building. The ability to control the state of a transaction for either a mobile client or business service (or both) provides maximum flexibility.
Code examples from this article
Eclipse plugin and mobile stub generator
NetBeans 4.2 plugin and stub generator
NetBeans version 4.2 (dev build) and NetBeans 4.2 Mobility Pack
"NetBeans Mobility Pack Quick Start Guide"
Nigel Warren is a co-founder of Net Caboodle and co-author, with Philip Bishop, of the books Java in Practice and JavaSpaces in Practice, both published by Addison Wesley Longman.
Philip Bishop is a co-founder of Net Caboodle and co-author, with Nigel Warren, of the books Java in Practice and JavaSpaces in Practice, both published by Addison Wesley Longman.
|
|