OASIS Business Transaction Protocol


The OASIS Business Transaction Protocol (or simply BTP) is a protocol for coordinating loosely coupled systems like Web services. BTP was the product of more than a year's work from several major vendors including BEA, Hewlett-Packard, Sun Microsystems, and Oracle, which set out to reconcile transactionality with systems consisting of loosely coupled autonomous parties. In short, the result of this collaboration has been to produce a specification which, although based on the traditional two-phase approach, is still suitable for supporting transactions on the Web.

The BTP Model

To facilitate transactional coordination for Web services, the first challenge that has to be overcome is establishing exactly what transactions mean in a Web services world. It's clear from our earlier observations on relaxing the ACID properties that the BTP model could not itself lay claim to ACID semantics (and as such simply become an XML-based re-cast of existing transaction systems). Instead, BTP derived precisely the level of transactionality that could be maintained without imposing semantics (e.g., strict two-phase locking, compensating actions) behind Web services endpoints. It also addressed how transactional coordination could be done in harmony with the general philosophy of today's Web services architecture.

From this general philosophy, a three-pronged attack on the problem was formed:

  1. BTP is an interoperation protocol that deals only with wire-level message exchanges, and the expected behavior of the senders and recipients of those messages. It does not presuppose any implementation details or architecture behind a service endpoint; it simply tries to coordinate those services via its messages using a two-phase approach (which importantly does not imply two-phase locking).

  2. BTP extends the traditional transaction model with additional transaction types (known as the atom and the cohesion) that are better suited to the autonomous, distributed, and unreliable nature of the Web.

  3. The coordinating party is no longer the exclusive decision-making body. BTP permits all parties involved in a transaction to specify their own qualification criteria within the scope of the transaction. This allows choices to be made up front as to a service's participation in a transaction, graceful retirements from running transactions, and a plethora of other useful features that all add up to reducing unnecessary and costly (in fact quite costly if we scale to the Web) network message exchanges between parties in a transaction.

Let's take these issues one at a time. First we must understand that BTP concerns itself only with message exchanges (and thus only implies the behavior at the message endpoints and does not specify its implementation). Since it is designed to deal with autonomous third parties that do not answer to a single design authority or even belong to the same trust domain, it can, of course, only have the authority to specify these message exchanges as we see in Figure 7-21.

Figure 7-21. Business transaction protocol: An interoperation protocol.

graphics/07fig21.jpg

The upshot of this approach is that any underlying technology can be used to build BTP infrastructure, and to build BTP-aware Web services. Provided the correct XML is sent on the wire, there is no prejudice against any back-end system. However, since there is no prejudice about the capabilities of any back-end system, we cannot second-guess the semantics, of the back-end transactional infrastructure. Since we do not know the back-end semantics we cannot absolutely guarantee ACID semantics with BTP. This is itself reflected in the choice of language used by BTP. Where a traditional ACID transaction processing system might use commit and abort, BTP uses confirm and cancel. Such language has been deliberately used to decouple the notion of two phases being equated to "ACID on the Web."

Second, although BTP cannot guarantee true ACID semantics through a true atomic transaction abstraction (because it has no knowledge of back-end semantics), it can guarantee consistency of the outcome messages sent to Web services participating in a transaction. In BTP, there is an abstraction that supports these semantics, called the atom. Any participants that are conducting work within the scope of an atom are guaranteed to see the same outcome (i.e. to confirm or cancel the transaction) as all other participants in the same atom (see Figure 7-22 where a uniform confirm is sent to all participants).

Figure 7-22. Atoms provide consistent outcomes.

graphics/07fig22.jpg

In addition to the atom, BTP also supports a second transaction type known as the cohesion, which allows the consistency of the outcome to be applied to specific distinct subsets of participants in a transaction, according to some business logic. This might at first seem a little strange, especially since there is a premium bestowed on traditional transaction processing systems that each participant in a transaction will see the same outcome. However, in an e-business environment, the ability to solicit interest from a number of parties and then proceed to complete a deal with a subset of those parties is commonplace. The BTP cohesion simply provides a means of operating under such a model in the Web services world, where we might interact with a number of partners' and customers' systems from the start of the transaction but ultimately as the work progresses only choose to carry our business forward with a subset of those systems. Those systems with which we eventually choose to do business will all receive messages to confirm their part of the transaction (known as the confirm-set), while those systems which our business decisions lead us to exclude from the transaction will receive cancel messages. This kind of use-case is demonstrated in Figure 7-23 where according to some business logic, a subset of the available participants is chosen to form part of the confirm set (and thus will ultimately make their work durable) while the other participants are cancelled.

Figure 7-23. Using a cohesion to create a narrow confirm set.

graphics/07fig23.jpg

Finally, unlike traditional transaction processing systems, the transaction coordinator ventures but one of many opinions voiced while the outcome decision for a transaction is being decided. In a traditional system this would be nothing short of heresy since the transaction coordinator is effectively godlike. In BTP, since we have no real level of control over the systems with which we're transacting, it makes sense for them to have their say and even more sense to listen. In a Web services world, because we have no real control over the services with which we interact, these services will have their own way in any case, so BTP does the sensible thing and at least listens to them and tries to accommodate their whims!

In BTP, this exchange of additional "small print" is achieved through the use of qualifiers, which are additional information that may be sent with each and every BTP message. There is a standard set of BTP qualifiers that mostly govern timing issues and this set is extensible for others to apply to their own particular application domains. Perhaps the most interesting and useful of the qualifiers that form part of a standard BTP implementation are those through which the transaction manager and participants can further decouple themselves from one another based on timeouts. The BTP specification provides three such mechanisms:

  1. Transaction Time Limit The client application suggests the length of time that the work phase of a transaction is likely to take. A participant can use this information to determine a situation where the transaction coordinator has become unresponsive and therefore where it might be reasonable to begin unilateral cancellation of work. This is exemplified in Figure 7-24.

    Figure 7-24. Transaction time limit qualifier.

    graphics/07fig24.jpg

  2. Inferior Timeout A participant can relay this qualifier to the transaction coordinator along with a positive vote (i.e., when it votes to prepare) during the first phase, as shown in Figure 7-25. This qualifier tells the coordinator how long the participant will remain prepared, and what action it will take when this period has expired.

    Figure 7-25. Inferior timeout qualifier.

    graphics/07fig25.jpg

  3. Minimum Inferior Timeout As per the example in Figure 7-26, the transaction coordinator specifies to a participant that it must agree to participate in the transaction for a certain period of time. If the participant knows that it cannot or will not do so, then it will respond negatively to any request to join the transaction.

    Figure 7-26. Minimum inferior timeout qualifier.

    graphics/07fig26.jpg

In short what BTP provides are transaction abstractions suited to B2B interactions with loose coupling, and it is deliberately non-prescriptive about what happens behind Web service endpoints. This all sounds rather good, but unless we can actually put it into practice it will remain a fine-sounding pipedream. Fortunately, there are BTP implementations available today, which means that we can implement transactional support for our Web services.

Implementing with BTP

Now that we understand the basics of transaction processing and the ins and outs of the BTP model, we're ready to look at how to implement applications and services with a BTP toolkit.

There are a number of BTP toolkits available on the market today. Although we use HP's software in this chapter, it is now being developed and marketed by Arjuna Technologies. See http://www.arjuna.com to download and get a free 30-day license. Choreology (http://www.choreology.com) also offer a BTP implementation via its Web site.


In this section, we're going to use one of the vendor toolkits (we've used HP's toolkit,[2] but the underlying principles are similar with other vendors' software) to build and consume transaction-aware Web services. But before we do, let's remind ourselves of the architecture in the context of a simple transactional scenario through which we'll build up our implementation.[3]

[2] All of the code samples in this article are written with HP's Web Services Transactions 1.0, though the general concepts embodied within the code are generic and should span most BTP APIs.

[3] Much of the following information is adapted from "Building Transactional Web Services with OASIS BTP," Webber, J., Web Services Journal, Vol. 2, Issue 10, October 2002, continued Vol. 2, Issue 11, November 2002. It is reprinted with permission of Sys-Con Media.

The diagram shown in Figure 7-27 is similar to many high-level Web services architectures. The only differences here are that one service, the transaction manager, has been singled out as being distinct from the other Web services (which we assume are responsible for some aspects of a business process), and the fact that we have chosen to identify two distinct categories of messages: control messages (used to control transactions) and application messages (which propagate application data around the system). Of course, now armed with our background in transactions, this shouldn't come as too much of a shock.

Figure 7-27. Architecture of a simple transactional Web service-based application.

graphics/07fig27.jpg

However, if it really were as simple as deploying a transaction manager service into the architecture, then this chapter really wouldn't be necessary. Unfortunately it's not that simple, or at least not quite that simple, as we shall see. To illustrate, it's convenient to use Figure 7-27 as a point of reference as we work through the architecture filling in the details. We'll work from left to right, from the client through to the Web services, and cover everything in between.

Consuming Transactional Web Services

Though Web services are a hot technology, we shouldn't lose sight of the fact that they exist to support business processes. With that in mind, the right place to start our investigation is most definitely at the client end of a system where the results of Web services interactions are brought together and ultimately where the value of a business process is focused. To place this in the proper context, it's useful to see an exploded view of the client-side infrastructure as per Figure 7-28.

Figure 7-28. BTP client-side infrastructure.

graphics/07fig28.jpg

In a non-transactional Web services-based application, the client process can be something as simple as a collection of calls (via proxies) to services that are involved in the business process. In a transactional Web services-based application, the same is, surprisingly enough, true with the caveat that the developer must demarcate any transactions that support business logic, as well as deal with application-specific calls. In this case the transaction demarcation is supported by the transaction API (the Client Tx API in Figure 7-28), whereas the business methods supported by service proxies remain (logically) free of any transactional infrastructure from the point of the client application developer. In fact, under the covers there is a mechanism that performs context associations with local threads of control within the client and messages passed between the client and (transactional) Web services. In Figure 7-28, this is the purpose of the Tx Context Interceptor.

Client API

The API provides the developer with the necessary tools with which to structure and control transactions within the application. The commands available to a developer in a transactional Web services environment are quite familiar to anyone who has used other transaction APIs in the past, with the caveat that BTP supports full control over both phases of the commit process and thus has a larger command set than we might otherwise envision. The common verbs (and by implication the methods that enact those verbs) for transaction demarcation are supported by the UserTransaction API:

  • Begin creates a new top-level transaction (or interposed transaction BTP does not natively support nesting) for either atomic or cohesive transactions.

  • Prepare instructs an atomic transaction to prepare its associated participating services when the transaction is to terminate.

  • Prepare Inferiors instructs a cohesive transaction to prepare one or more of its participating services at transaction termination time.

  • Confirm instructs an atomic transaction to confirm all of its participating services, confirms all participating services that voted to confirm in the case of a cohesive transaction.

  • Cancel instructs all participating services in an atomic transaction, or those services specified in the parameter to the method call in a cohesive transaction, to cancel.

In addition to these demarcation verbs, there are a number of other commands through which we can inquire about a transaction:

  • Status asks the transaction manager to return the state (e.g., committed, preparing) of the current transaction.

  • Transaction type exposes the type of the current transaction (i.e., atom or cohesion).

  • Transaction name exposes the name of the current transaction in string form.

There are two verbs that allow advanced manual transaction management, which are:

  • Suspend disassociates the current thread from the current transaction.

  • Resume (re)associates the current thread with the current transaction.

Anyone who has previously worked with transactions will immediately find himself or herself at home with this API, since it is in the same spirit as other transaction APIs, as we can see by considering an example such as the code fragment shown in Figure 7-29.

Figure 7-29. Controlling atoms from a Java Web service client.
 // obtain a UserTransaction object (assume it is previously // initialized) userTransaction = UserTransactionFactory.getTransaction(); // obtain references to the web services we are going to use: restaurant = new RestaurantService(); taxi = new TaxiService(); theatre = new TheatreService(); // Start a new transaction, using a simple Atom userTransaction.begin(com.hp.mw.xts.TxTypes.ATOM); // now invoke the business logic restaurant.bookSeats(3); theatre.bookSeats(3, 2); taxi.bookTaxi(); // prepare the transaction (1st phase of two phase // coordination) userTransaction.prepare(); // confirm the transaction (2nd phase of two phase // coordination) userTransaction.confirm(); 

In the code shown in Figure 7-29, we see a Java client driving an atom being used to ensure a consistent outcome across calls to the Web services shown in Figure 7-27. Initially we obtain a reference to an instance of UserTransaction from a (previously initialized) UserTransactionFactory, which we then use to delimit the scope of the single transaction in our application. Our atomic transaction is started by calling the begin(…) method on the user transaction API and specifying the type of transaction as an atom. From now on, the business logic is straightforward and contains no further transaction control primitives; we simply go ahead and make the bookings that we want for our night out through the book (…) methods of the service proxies we created. Once the business logic has completed, we can terminate the transaction by calling prepare(…) and confirm(…) which, in the absence of failures, should confirm to all parties that they should henceforth honor all of our booking requests. If there are failures, then all parties are informed and should take the necessary steps to undo any work undertaken on our behalf, while the client application will receive an exception that details what exactly has gone wrong.

The most interesting aspect of this example is that is shows just how simple and non-invasive it can be to wrap work with Web services within a transaction. In fact the business logic aspects of the code would be the same regardless of whether or not transactions are used.

Under the Covers: BTP's Two-Pipe Model

To support transactional Web services-based applications, BTP utilizes two distinct types of messages that the client application exchanges with Web services. The first of these messages is exchanged exclusively within the transaction infrastructure. The other type consists of messages that the client exchanges with business Web services onto which BTP messages might be piggybacked.

The messages that the application exchanges with the transaction infrastructure are encapsulated by the primitives supported by the client API. For example, a begin(…) method being executed by the client causes a corresponding BTP begin message to be sent to a transaction manager via the SOAP server, and for response messages from the transaction manager to be processed in the reverse order. This is shown in Figure 7-30, and a sample BTP message (begin) is shown in Figure 7-31. The only slightly unusual aspect to this example is that the response to begin messages (and it is only begin messages this occurs with) is cached for later use so that local threads of execution can be associated with the BTP transaction under which its work is being executed.

Figure 7-31. A BTP Begin message.
 <?xml version="1.0" encoding="UTF-8" ?> <SOAP:Envelope SOAP:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">   <SOAP:Body>     <btp:begin transaction-type="atom"       xmlns:btp="urn:oasis:names:tc:BTP:1.0:core" />   </SOAP:Body> </SOAP:Envelope> 
Figure 7-30. Creating and consuming BTP messages at the client side.

graphics/07fig29.jpg

With application messages, the situation is a little different. Unlike BTP messages where the message contents travels in the body of the SOAP envelope, when application messages are sent application-specific content travels in the body while any BTP content is relegated to the header part of the envelope. We can see this in Figure 7-32 where the SOAP body carries the application payload, while the header is used to carry the BTP context.

This scheme works well since most SOAP stacks are well equipped to perform efficient header processing, and placing the BTP content, including the transaction context, in the header means that SOAP actors can pick out the parts of the header space of interest without having to parse the whole application payload. From a development point of view, most SOAP servers support pluggable header processors, which means that building BTP context processing into your infrastructure should be straightforward, and any BTP toolkit should provide useful components to ease the process. To demonstrate that point, let's take a look at the general client-side architecture (which is based on Apache Axis in the toolkit we've used), as per the examples in Figure 7-33 and Figure 7-34.

Figure 7-32. An application message with BTP context.
 <?xml version="1.0" encoding="UTF-8" ?> <SOAP:Envelope SOAP:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"   xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">   <SOAP:Header>     <btp:messages xmlns:btp="urn:oasis:names:tc:BTP:1.0:core">       <btp:context>         <btp:superior-address>           <btp:binding-name>soap-http-1</btp:binding-name>             <btp:binding-address>               http://example.com/btpservice             </btp:binding-address>           </btp:superior-address>           <btp:superior-identifier>             12fa6de4ea3ec           </btp:superior-identifier>         <btp:superior-type>atom</btp:superior-type>       </btp:context>     </btp:messages>   </SOAP:Header>   <SOAP:Body>     <ns:booking xmlns:ns="http://btp.restaurant.org/">       <seats xsi:type="xsd:int">99</seats>     </ns:booking>   </SOAP:Body> </SOAP:Envelope> 
Figure 7-33. Client-side request processing.

graphics/07fig30.jpg

Figure 7-34. Client-side response processing.

graphics/07fig31.jpg

Figure 7-33 shows the outward path of a call to a Web service, starting from the left with the local method call onto a service proxy. The call then follows the logical path of being converted to the appropriate SOAP body that contains the application payload before it progresses to the outgoing context handler. The context handler takes advantage of the fact that the information supplied in response to the BTP begin message was recorded, and is able to produce a BTP context from that data which it duly inserts into the SOAP envelope's header. If there is no contextual data stored for the current thread (i.e., it isn't part of a transaction or the transaction has been deliberately suspended), then the context insertion is simply bypassed.

For return messages, the strategy is simply the reverse as shown in Figure 7-34 where the flow is from right to left. Responses are quickly scanned to see if they contain any BTP context entries in their headers. If context data is present, it is stripped out of the message and may be used to resume the transaction locally by associating the current thread, while the rest of the message passes through to the service proxies. Once at the service proxies, the local method call returns control to the client, which is unaware of all of the additional processing that has occurred on its behalf.

Having reached the point where we can send application messages with BTP contexts, as well as BTP messages themselves, we're able to follow the messages as they travel across the wire. As we follow the cables, we are inevitably led to yet more Web services.

Transactionalizing Web Services

To transactionalize a Web service with BTP is perhaps something of a misnomer, since BTP does not deal with transactional Web services per se. It chooses instead to partition Web services into two distinct types to enable clear separation of business services and their associated transaction-oriented participants. Business services are similar to client applications in that there is no inherent transactionality directly associated with them they simply exist to host and expose business logic. On the other hand, the participants associated with business services are essentially business logic agnostic and deal only with the transactional aspects of service invocations. This dichotomy turns out to be quite useful since it means that existing services can be given transactional support without necessarily performing any invasive procedures on them. The fact that non-transactional Web services can be given transactionality without having to rebuild the service is a real plus, which also means that the transactional and business aspects of a system can be evaluated and implemented independently. Having added some more pieces of the puzzle, we can now reshape the global BTP architecture, resulting in Figure 7-35.

Figure 7-35. Booking a night out with BTP-enabled infrastructure.

graphics/07fig32.jpg

Figure 7-35 typifies a BTP rollout, showing how the endpoint of each BTP actor fits into the global model. Here we see the services that expose business logic to the Web are supported by other services, the BTP participants, which deal with the transaction management aspects of business Web services and, most importantly, how there is a clean separation between the two kinds of service. However, there is clearly some overlap even at this level since application messages carry BTP contexts whenever service invocations are being made within the scope of a transaction. It is here that the business logic and transaction domains collide, albeit gently.

Supporting Infrastructure

For business Web services, most of the interesting work from a transactional perspective happens under the covers. Like the client application, Web services benefit from advances in SOAP server technology that support header processing before the application payload of a SOAP message is delivered. For BTP-aware Web services, SOAP header processing can be utilized to insert and extract BTP contexts on behalf of Web services in a reciprocal fashion to the way header processing is performed at the client side. Since header processing is non-invasive to the service-level business logic, we can see that the impact of making a service transactional with BTP being minimal. A typical transactional infrastructure for a Web service stack is presented in Figure 7-36 and Figure 7-37.

Figure 7-36. Web service incoming context handler.

graphics/07fig33.jpg

Figure 7-37. Web service outgoing context handler.

graphics/07fig34.jpg

Figure 7-36 depicts what happens when a Web service receives a SOAP request. If the request doesn't carry a BTP context, then it is simply passed through the incoming context handler to other registered handlers (if there are any) and will eventually deliver its payload to the service. If, however, the request carries a BTP context, then the context is stripped out of the header of the incoming message and is associated with the thread of execution within which the service's work will be executed. To achieve this, the handler resumes the transaction, using elements from the Transaction Manager part of the API we saw earlier, which effectively associates (or re-associates if this is not the first time the same context has been received) the work performed by the service with a BTP transaction.

When returning from a service invocation, the reverse process occurs as shown in Figure 7-37. The message from the service passes through the outgoing context handler that checks to see if there is a transaction associated with the work that took place to produce the message. If the work was performed by a thread within the scope of a transaction, then the BTP context is inserted into the header of the message and the transaction is suspended, which effectively pauses its work for the service until further messages with a matching context are received.

While none of this is especially rocket science or brain surgery, it does serve to reiterate that BTP-enabling Web services is a non-invasive procedure. However, at some point every BTP deployment has to interact with existing infrastructure, and it is here that we enter a more intricate phase of Web service development.

Participants

Participants are the last piece of implementation in our BTP architecture. Although the high level view of how participants fit into the global BTP architecture has been discussed, we haven't yet studied the anatomy of a participant, which is an oversight we are about to correct.

Participants are the entities that act on behalf of business Web services in matters regarding transactionality. From a Web services point of view, they are equipped to deal with message exchanges with the transaction manager, whereas from an enterprise's perspective they are implemented to propagate transaction messages and outcomes to back-end systems. While that sounds like hard work, a toolkit will simplify matters by offering an interface that your participant will implement to become part of the participant stack. The participant stack is shown in Figure 7-38, and (a cut down view of) the interface which constitutes the API for the participant stack from the developer's point of view is shown in Figure 7-39.

Figure 7-38. The participant stack.

graphics/07fig35.jpg

Figure 7-38 shows the conceptual view of a participant (minus the back-end plumbing that we shall see later). It's conceptually a straightforward document exchange-based Web service where the messaging layer understands BTP messages and invokes methods on the user-defined participant (which has a known interface) in accordance with the type and content of the messages it receives. Any returns from the participant are shoehorned into BTP messages and sent back through the SOAP infrastructure.

Figure 7-39. The (Java) participant interface.
 public interface Participant {     public Vote prepare (Uid id, Qualifier[] qualifiers)         throws GeneralException, InvalidInferiorException,             WrongStateException, HeuristicHazardException,             HeuristicMixedException;     public void confirm (Uid id, Qualifier[] qualifiers)         throws GeneralException, InvalidInferiorException,             WrongStateException, HeuristicHazardException,             HeuristicMixedException,             InferiorCancelledException;     public void cancel (Uid id, Qualifier[] qualifiers)         throws GeneralException, InvalidInferiorException,         WrongStateException, HeuristicHazardException,         HeuristicMixedException, InferiorConfirmedException;     public void contradiction (Uid id,                                Qualifier[] qualifiers)         throws GeneralException, InvalidInferiorException,             WrongStateException;     // Other methods... } 

The participant API effectively shields participant developers from having to understand the BTP messages that participants consume, though this shielding is not entirely "bullet proof" since some understanding of how and when the methods in a participant are called is still required. Figure 7-39 shows the more important methods that an implementer has to write in order to create a participant. As we might expect, these methods correspond to the messages that are exchanged between transaction manager and participant (which is itself identified by a unique ID or Uid in the API). As such if we have an understanding of BTP (which we should have to write a decent participant), then the methods are quite self-explanatory. However, here is a brief overview:

  • prepare(…) The prepare method begins BTP's two-phase confirm protocol. During this method a participant typically checks with its back end to see whether it can proceed with a transaction on behalf of the service it is representing, and returns a Vote which causes an appropriate response to be propagated down the stack and ultimately to the transaction manager. Note if the participant votes to cancel, it may not receive further messages from the transaction manager from here on, and its back end may take suitable abortive action.

  • confirm(…) Confirming a participant causes the second phase of the two-phase confirm to occur. At confirm time the participant relays to its back end that any updates to state should be made durable.

  • cancel(…) Cancel is the opposite of confirm, whereby a participant will typically relay to its back end that it should undo, forget, or otherwise reverse any changes that have been made to system state by the business logic residing in the associated Web service either by traditional locking strategies in a trusted domain or by a compensative strategy otherwise.

  • contradiction(…) Contradictions are the BTP terminology for what are traditionally known as heuristics. If a service back end finds itself in a situation where it has done the opposite of what it has been asked to do by the transaction manager (e.g., it has cancelled when it should have confirmed or vice versa), and cannot mask the fault, it will send an exception to the transaction manager. The transaction manager will evaluate the situation from a global perspective and may need to inform other participants of the contradiction that has occurred. If that is the case, then a participant will learn about contradictions that have occurred when its contradiction method is invoked. At that point a participant typically tries to instigate compensative action, though to fully recover from a contradictory situation help from outside the system (even human help) may be required as per traditional heuristic outcomes.

One final intricacy for participants is the sending and receiving of qualifiers. As was discussed earlier in the chapter, qualifiers are a neat feature from BTP which derive from the fact that the BTP transaction manager is not godlike, as are its equivalents in other transaction management models, but instead accepts the possibility that other parts of the system might justifiably want to help out in the decision making process. In the API, qualifiers are delivered to the participant implementation through the Qualifier[] qualifiers parameter (where the transaction manager gets the chance to state its additional terms and conditions) and are returned from the prepare(…) method as part of the Vote (where the participant then gets to respond with its own terms and conditions). Qualifiers are a real help when it comes to Web services transactions because in a loosely coupled environment, it is invaluable information to know from the client side that the party you're communicating with will only be around for so long, or to be able to specify from the participant side that your party won't hang around while others procrastinate. The downside is, of course, that as the developer of a participant you will be expected to write code that handles any qualifiers and propagates them onto the equivalent mechanisms in your back-end systems but the reward in terms of integration with the Web services architecture is worth the effort.

Compensating Actions: A Strategy for Participant Implementation

The overriding theme of Web services transaction is that locking for any significant period of time simply is not suitable for Web-scale architecture. Therefore the strategies that we choose to provide to the abort facility of a participant should not (outside of a specific trusted domain) involve the use of locking and shadow writes because of the potential denial of service consequences.

If we accept that we will not hold long-lived locks on data, then we also accept that we cannot simply discard shadow updates and release locks (known as backward error recovery) to implement our cancel operation.

Since we cannot hold locks for an extended period, we must only hold locks for that instant at which we update a piece of state, and to update that state directly rather than update a shadow copy. Now in the event of a cancel request reaching our participant, we must use a forward error recovery strategy to reverse the changes that we optimistically committed to durable storage that is, we execute a compensating action.

A compensating action is in effect the reciprocal function to the original business logic, which has the effect of returning the system to the same state as before the business logic. For example, in our debit-credit system, a compensating action would durably record the intended debits and credits for each account, and in the event of a cancel would simply run a debit where there was a credit, and a credit where there was a debit, as shown in Figure 7-40.

Figure 7-40. A compensative Cancel for the debit-credit example.

graphics/07fig36.jpg

Although compensation does provide a solution to the problem of exposing long-term lockable resources to the Web, it is not itself without drawbacks:

  • Compensation may not always be able to return the system to a state completely identical to the system state before the transaction ran. For example, if the debit-credit operation had been executed by placing checks in the mail, it may not be able to fully compensate for this action within the system itself.

  • Since running a compensating action is actually running a piece of business logic, compensations may be as computationally expensive to execute as any other piece of business logic which may place a heavy burden on any underlying infrastructure.

  • Forward error recovery is more expensive to design and implement than backward error recovery mechanisms since the compensating actions will have to be developed for each permissible operation, rather than using a generic mechanism. Furthermore, compensating actions may generally not be as trivial as those seen in the debit-credit example.

Thus the cost of adopting a forward error recovery strategy with compensating actions is, in terms of additional analysis and implementation, compared with backward error recovery mechanisms. The benefits though are clear, insofar as without such a strategy our transactional Web services can only safely be advertised to and consumed by our trusted infrastructure, which very much limits scope for real B2B interactions over Web services.

Integrating Participants and Services

Context-work association is not the only place where there is overlap between BTP infrastructure and the Web services that it supports. In fact it is the integration of BTP participants with service back ends that is the most important and intricate aspect of a BTP deployment. Unlike service-side context handling, sadly there are no stock answers to the problem of participant-service integration, because the strategy adopted will depend on the existing transactional back-end infrastructure that the service itself relies upon. However, we can mitigate this to some degree by using tools provided to the back-end developer in the form of an API that takes care of the common back-end tasks. In the same spirit as the client API, two further verbs that deal with enlisting and removing participating services from a transaction are supported by the TransactionManager API and may be used from the service's back-end. They are:

  • Enrol Enlists a specific participant with the current transaction.

  • Resign Removes a specific participant from the current transaction.

Using this service-side API and in keeping with the theme of non-invasiveness that is so much in the spirit of BTP, we would ideally like to deploy systems that do not disturb existing (working) Web services. Fortunately, there are means and ways of doing just that, as shown in Figure 7-41.

Figure 7-41. Integrating services, back-end infrastructure, and participants.

graphics/07fig37.jpg

Figure 7-41 depicts the back end of a Web service, and is simply the continuation of the diagrams shown in Figure 7-36 and Figure 7-37. We can assume there will be some kind of transactional infrastructure in the back end of most enterprise-class Web services, and for the sake of simplicity here we can assume it is something like a database.

The good news is that even without BTP transactions thrown into the mix, the exposed Web service will still need to talk to its own back-end systems. It is therefore possible to intercept the interactions between the service and the back end to suit our own purposes. A useful strategy in this situation is to wrap the service's database provider within our own provider that supports the same interface, but is also aware of the BTP infrastructure.

In this example, our database provider wrapper has access to BTP context information from the header processing logic embedded in the service's stack, and is aware of the participant service that performs BTP work on behalf of the business service. Armed with such knowledge, the database wrapper can enroll a participant into the BTP transaction through the enroll operation supported by the API, which causes a BTP Enroll message exchange to occur with the transaction manager. When there are no upsets during the enrollment of the participant, BTP messages can now be exchanged between the transaction manager and the participant, ensuring that the participant knows exactly what's happening in the BTP transaction at all times. Having that knowledge allows the participant to mediate between the transactional semantics (if any) of the service's database access and the activity in the BTP transaction. Such arbitration may not be trivial and will certainly require some domain expertise, since the participant implementation will have to reconcile BTP semantics with those of the service's own back-end transaction processing model. For example, a participant implementation might choose to perform a simple mapping of BTP messages to its native database, queue, or workflow system equivalents, or the participant might choose to take an optimistic approach and immediately commit all changes to the database and perform a compensating action in the event of a failure. What implementers must remember is that there is no absolute right or wrong, just participant implementations that work well for a given system and those that don't, and that time spent analyzing use-cases up front will pay dividends in the long run.

When two or more participants from a single organization are enrolled within the scope of the same transaction, they can collude with one another and use the knowledge of the transaction to alter the business-level outcomes. For instance, if a participant for a nut-vending service and a bolt-vending service from the same organization are both enrolled within the scope of the same atom, they know that nuts and bolts will be purchased. This allows the business logic to offer more competitive pricing based on the knowledge that either both will be purchased or neither. Conversely, if the participants are enrolled within the scope of a cohesion, there are no such guarantees and pricing may be altered accordingly.


The Transaction Manager

It might seem a little strange to mention the transaction manager at such a late stage and with so little detail given its importance in the architecture. This is especially apparent given that the transaction manager is, after all, the component on which all other components depend. The paradox is that the transaction manager is simply the least interesting part of the architecture from an application or Web service developer's point of view. It is simply a SOAP document exchange-based Web service that implements the BTP state machine and suitable recovery mechanisms so that transactions are not compromised in the event of failure. From a development point of view, a BTP transaction manager is simply a black box, deployed somewhere on the network to enable the rest of the infrastructure to work, and it falls only to those who have chosen to implement BTP toolkits to worry about its internals.

Bringing It All Together: A Cohesive Example

We've seen the BTP architecture, the SOAP plumbing and have touched on the transaction model that BTP supports. Now we can draw together the many different aspects into a more powerful illustration. For this example, we're going to revisit the night out application whose architecture we developed earlier in Figure 7-35. That code interacted with a number of Web services within the context of a BTP atom, thus ensuring a consistent outcome for the services in the transaction. We use a similar pattern for this cohesion-based example, though since cohesions are more flexible than atoms, we have to work just a little harder. We use approximately the same use-case except we'll spice things up a little by allowing either the theatre or the restaurant service to fail, with the intention being that as long as we get a night out, we don't care much what we actually do. The code that shows how to program with cohesions is in Figure 7-42.

Figure 7-42. Using cohesions from a Web service client.
 // obtain a UserTransaction object (assume the factory is // initialized) UserTransaction userTransaction =   UserTransactionFactory.getTransaction(); // Obtain references to the web services we are going to use: RestaurantService restaurant = new RestaurantService(); TaxiService  taxi = new TaxiService(); TheatreService = new TheatreService(): // In a cohesion, we have to make an application-level mapping of // services to participants (outside of BTP scope). Our example // services support this. String restaurantParticipantID = restaurant.getParticipantID(); String theatreParticipantID = theatre.getParticipantID(); String taxiParticipantID = taxi.getParticipantID(); // Start a new transaction, using a Cohesion userTransaction.begin(com.hp.mw.xts.TxTypes.COHESION); // Now invoke the business logic restaurant.bookSeats(3); theatre.bookSeats(3, 2); taxi.bookTaxi(); // Prepare (all of) the participants (i.e. with null parameter) StatusItem[] statusItems = userTransaction.prepare_inferiors(null); // Iterate over the statuses, and make sure the taxi's pledged to // honor the booking boolean restuarantOK = false; boolean taxiOK = false; boolean theatreOK = false; for(int i = 0; i < statusItems.length; i++) {     if(statusItems[i].inferiorName().equals(restaurantParticipantID))     {         if(statusItems[i].status() == TwoPhaseStatus.PREPARED)         {             restaurantOK = true;         }     }     else if(statusItems[i].inferiorName().equals(taxiParticipantID))     {         if(statusItems[i].status() == TwoPhaseStatus.PREPARED)         {             taxiOK = true;         }     }     else if(statusItems[i].inferiorName().equals(theatreParticipantID))     {         if(statusItems[i].status() == TwoPhaseStatus.PREPARED)         {             theatreOK = true;         }     } } // If we can't get a taxi, then we have to call the whole event off. if(!taxiOK) {     ut.cancel(null); // Cancel everything } else if(restaurantOK || theatreOK) {     ut.confirm(); // Confirm whatever is available } else {     ut.cancel(null); // Can't get anywhere to go, cancel everything. } 

The code in Figure 7-42 follows a similar pattern to the atom example shown in Figure 7-29. We start our transaction and interact with the services via proxies as usual. The important difference in the cohesion scenario compared to the atom example is that the application must deal directly with the participants that support the services, whereas with atoms the details of any service's participants are encapsulated by the transaction manager.

There are various ways in which we might obtain the names of the participants that the services enroll into the transaction, but unfortunately the BTP specification does not provide any definitive means of obtaining that information (though it does allow participants to be given "friendly names" through the use of a qualifier called Inferior Name to help out in such situations). In this example, we allow the services themselves to report on the names of their participants through their participantID() operations, though any form of a priori knowledge via human or automated means (like UDDI-based discovery) could be feasibly substituted. With time and experience, patterns for this kind of work will emerge and become commonplace.

Once we have the names under which the participants are enrolled into the cohesion, we can then use them to ascertain what decisions the services' participants make when we terminate the transaction. In this example, we iterate over the decisions that were returned by the prepare_inferiors(…) call to find out if the taxi and at least one other service are prepared to make forward progress. If our conditions are met, we confirm the transaction with the confirm() method which confirms all those services' participants that agreed that they could meet our requirements (those that voted to confirm) and cancels any services that could not meet our requirements (those that voted to cancel). Conversely, if our overall requirements cannot be met, then we can immediately cancel the transaction and the participants will all be instructed to undo the work of their associated Web services.

The power of cohesions arises from the fact that we are at liberty to make choices about who will participate in our transaction right up until the point that we try to confirm it. In fact, we could have used several taxi services in this example to ensure that we have at least one travel option and simply cancelled those that we didn't want before we came to confirm the cohesion.

Using cohesions, as implementers of the client application we are at liberty to structure our transactions as we see fit to suit the problem domain. For instance, if we knew that we absolutely had to take a taxi to meet friends at the restaurant, but were not sure whether we wanted to go to a show afterward, we could wrap the taxi and restaurant booking operations within an atom (or indeed wrap several independent taxi and restaurant bookings into several atoms) and enroll that atom into a cohesion along with the participant for the theater Web service. In this case we are guaranteed to get the taxi and restaurant bookings together (or not at all), while we have some leeway in terms of whether we decide to go to the theater.

BTP: In a Nutshell

Though BTP itself is a sophisticated protocol, from the point of view of an implementer much of its detail is handled by supporting toolkits. Creating applications that drive BTP transactions is straightforward because of the traditional-looking and intuitive API. Though making Web services transactional is a little trickier, BTP toolkits will help to simplify things to a great extent by providing much of the Web service-side infrastructure. The only tough challenge for implementers is the construction of participants, which does require a more thorough understanding of transactional architectures to get right.



Developing Enterprise Web Services. An Architect's Guide
Developing Enterprise Web Services: An Architects Guide: An Architects Guide
ISBN: 0131401602
EAN: 2147483647
Year: 2003
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net