|
Transactions
Detailed Introduction to the Transaction Service
ForewordLike the Task Service, the Transaction Service is a foundation of the Hemi Framework in that it was one reason the MDI toolkit was discontinued in favor of the M3 design in 2002 (which became Engine, and now Hemi). While the Task Service underwent several architectural designs, the mechanism by which disimilar objects exchanged stateful data was extracted as the Transaction Service. IntroductionTransactions are stateful communication bands shared by dissimilar objects. Objects registered as Transaction Participants share managed state for a named Transaction Packet. Transactions are served by request to each participant until all participants indicate successful or termination status. Related ReadingTransaction Service IntegrationThe following Hemi JavaScript Framework services and libraries use the Transaction Service.
And, the following services use the Task Service, thus indirectly use the Transaction Service.
TransactionsA transaction is comprised of a Transaction Packet and participating Transaction Participants. Any object registered with the Transaction Service may join a transaction by participating with the packet. Transaction Processing FlowThe Transaction Service operates in a warm standby mode, waiting for an instruction to serve a specified transaction. The service starts automatically when included. The following flow describes the lifecycle of participation and packets. Packet Flow
Participation Flow
Transaction PacketsTransaction Packets describe overall state, variant data, and Transaction Participant processing state. Transaction Packet APIProperties
Methods
Packet Data ModelTransaction Packets define a placeholder for variant data. The joinTransactionPacket method injected by addServiceAPI uses this placeholder to add a second level model:
In the Transactional Application Components example, this model is referenced to retrieve a value from the object that served the transaction. Transaction ParticipantsTransaction Participants are registered framework objects that implement the required Transaction Participant API. The required API may be manually define, instrumented per object, or provided by a base implementation. Transactional ComponentsApplication Components are instrumented with the Transaction Service API, and the addServiceAPI method was originally extracted from helper implementations defined in the Application Component library. Application Components provide the quickest way to work with transactions. Example Transactional ComponentThe following example demonstrates how two components may participate in a common transaction. First, create a component that shares participation for the reflect transactions, and that listens for onkeyup events and serves a transaction packet named keypunch. Assume this component is saved to Hemi/Components/component.reflector.xml. <application-components>
<application-component id = "reflector" participant-id = "reflect">
<![CDATA[
component_init : function(){
/// TODO: Fixed in 3.2; add onkey* to the default event capture list for components
this.scopeHandler("keyup",0,0,1);
Hemi.event.addEventListener(this.getContainer(),"keyup",this._prehandle_keyup)
},
_handle_keyup : function(){
/// The double .data.data is from using the convenience addServiceAPI version, which automatically creates adds a common object for the packet data property, including its own data property
this.getPacket("reflect").data.data = this.getContainer().value;
this.serveTransaction("keypunch",this);
},
component_destroy : function(){
Hemi.event.removeEventListener(this.getContainer(),"keyup",this._prehandle_keyup);
}
]]>
</application-component>
</application-components>Second, create a component participates in the same reflect transaction and that responds to a named transaction packet keypunch by changing its contents to the value. This example assumes the serving component transcribed a value to the packet. Assume this component is saved to Hemi/Components/component.reflected.xml <application-components>
<application-component id = "reflected" participant-id = "reflect">
<![CDATA[
_handle_keypunch : function(s, v){
Hemi.xml.setInnerXHTML(this.getContainer(),v.data.data);
}
]]>
</application-component>
</application-components>Third, defined the two components for an input field and a span element. <input type = "text" component = "reflector" /> <p>Reflected Value: <span component = "reflected"> </span></p> When a value is typed into the input field, it is then printed in the span tag. In this case, the two components are connected by a named transaction. Furthermore, the components could just as easily branch out to other transactional channels. Required APIInstrumented APIThe Transaction API may be instrumented for an existing object using the addServiceAPI method. The instrumented API injects the required API as needed, and includes additional helper methods. The injected API includes:
Instrumented API Example
/// Given some existing non-framework objects
///
var oObject1 = {
_handle_transaction : function(TransactionService, TransactionPacket){
/// All participating transaction packets
}
};
var oObject2 = {
_handle_transaction : function(TransactionService, TransactionPacket){
/// All participating transaction packets
},
_handle_testpacket1 : function(TransactionService, TransactionPacket){
/// Only the named participating transaction packet
}
};
/// Prepare for the framework
Hemi.prepareObject("type1","1.0",true, oObject1, true);
Hemi.prepareObject("type2","1.0",true, oObject2, true);
/// Register objects and instrument the service API
Hemi.tranaction.service.register(oObject1, true);
Hemi.tranaction.service.register(oObject2, true);
/// Join the objects to a transaction
oObject1.joinTransactionPacket("demo");
oObject2.joinTransactionPacket("demo");
/// Serve the transaction from Object 1, and note to serve to itself
oObject1.serveTransaction("testpacket1", "source", 1);
/// Object2 will receive invocations to both _handle_transaction and it's _handle_packettype, _handle_testpacket1Manual API DeclarationManually specifying the transaction API eschews the convenience methods of the injected API for greater control over packet data, processing, and allowing for multiple transaction service scopes. It also requires manually configuring the packet and joining participants to the packet. Manual API Examplevar oObject1 = {
startTransaction : function(TransactionService, TransactionPacket){
},
endTransaction : function(TransactionService, TransactionPacket){
},
doTransaction : function(TransactionService, TransactionPacket){
var bKeepServing = true;
return bKeepServing;
}
};
var oObject2 = {
startTransaction : function(TransactionService, TransactionPacket){
},
endTransaction : function(TransactionService, TransactionPacket){
},
doTransaction : function(TransactionService, TransactionPacket){
var bKeepServing = true;
return bKeepServing;
}
};
/// Prepare for the framework
Hemi.prepareObject("type1","1.0",true, oObject1, true);
Hemi.prepareObject("type2","1.0",true, oObject2, true);
/// Register objects and instrument the service API
Hemi.tranaction.service.register(oObject1, true);
Hemi.tranaction.service.register(oObject2, true);
/// Mockup some packet data
///
var oData = {
id:"123",
name:"Demo"
};
/// Open the transaction, and assign oObject1 as the owner
var sTransId = Hemi.transaction.service.openTransaction(
"testpacket1", oObject1, oData
);
/// Get the packet
var oPacket = Hemi.transaction.service.getPacketByName("testpacket1");
/// Instruct oObject2 that the transaction is starting (startTransaction callback)
oPacket.setBlockStartTransaction(false);
/// Add oObject2 to the transaction
Hemi.transaction.service.addTransactionParticipant(oObject2, oPacket);
/// Serve the transaction to the owner and all participants
Hemi.transaction.service.serveTransaction(oObject1, oObject1.getObjectId(), false);Details |