Previous | Contents | Index |
This chapter contains suggestions for designing and implementing a new client or server application using the C++ foundation classes. It also includes code examples from the C++ bookorder and processing sample application included in the examples directory of the RTR kit. This sample application shows how to implement a derived-receive model. Topics include:
When creating a new client or server application:
The steps described in this section for client and server applications implement a polling client application and an event-driven server application. These steps include code examples that are part of the book processing sample application for ordering books and magazines.
While the steps in this section are representative of client and server
applications, there are design alternatives. A sampling of these design
alternatives is provided in later sections of this chapter.
2.2.1 Implementing a Server
To implement a server application, you:
For example, the typical steps for implementing a server are the following:
// Create a partition that processes ISBN numbers in the // range 0-99 unsigned int low = 0; unsigned int max = 99; RTRKeySegment KeyZeroTo99( rtr_keyseg_unsigned, sizeof(int), 0, &low, &max ); |
RTRPartitionManager PartitionManager; sStatus = PartitionManager.CreateBackendPartition( ABCPartition1, ABCFacility, KeyZeroTo99,false,true,false); print_status_on_failure(sStatus); |
SimpleServerEventHandler *pEventHandler = new SimpleServerEventHandler(); SimpleServerMessageHandler *pMessageHandler = new SimpleServerMessageHandler(); |
RTRServerTransactionController *pTransaction = new RTRServerTransactionController(); |
....sStatus = pTransaction->RegisterFacility( pFacilityName ); assert(RTR_STS_OK == sStatus); ....sStatus = pTransaction->RegisterPartition( pPartitionName ); assert(RTR_STS_OK == sStatus); sStatus = pTransaction->RegisterHandlers( pMessageHandler, pEventHandler ); assert(RTR_STS_OK == sStatus); |
RTRData *pDataReceived = NULL; |
while (true) { sStatus = pTransaction->Receive(pDataReceived); print_status_on_failure(sStatus); sStatus = pDataReceived->Dispatch(); print_status_on_failure(sStatus); } |
RTRServerTransactionController * pController; pController->AcceptTransaction(); |
void ABCSHandlers::OnPrepareTransaction( RTRMessage *pRTRMessage, RTRServerTransactionController *pController ) // Check to see if anything has gone wrong. If so, reject the // transaction, otherwise accept it. if (true == m_bVoteToAccept) { pController->AcceptTransaction(); } else { pController->RejectTransaction(); } |
void ABCSHandlers::OnAccepted( RTRMessage *pRTRMessage, RTRServerTransactionController *pController ) { pController->AcknowledgeTransactionOutcome(); return; } |
To implement a client application, you:
In more detail:
RTRClientTransactionController *pTransaction = new RTRClientTransactionController(); |
sStatus = RegisterFacility(ABCFacility); print_status_on_failure(sStatus); if(RTR_STS_OK == sStatus) { m_bRegistered = true; } |
class MyApplicationMessage : public RTRApplicationMessage MyApplicationMessage *pMessage1 = new MyApplicationMessage() |
sStatus = pTransaction->SendApplicationMessage(pMessage1); print_status_on_failure(sStatus); |
pTransaction->AcceptTransaction(); |
The following server application example shows the steps in setting up the infrastructure for running an application to process transactional requests. There is a server.h and a server.cpp file.
In the server.h file, after including the necessary header files and defining pointers to RTR facility and partition names, the business class, deriving from the RTRServerTransactionController class is defined. This includes declaring a transaction controller constructor and destructor.
#include <iostream.h> #include <rtrapi.h> #include <assert.h> const char *ABCFacility = "MyFacility"; const char *ABCPartition = "MyPartition"; class SRVTransactionController: public RTRServerTransactionController { public: SRVTransactionController(); ~SRVTransactionController(); private: }; SRVTransactionController::SRVTransactionController() { cout << "In Server Transaction Controller constructor " << endl; } SRVTransactionController::~SRVTransactionController() { cout << "In Server Transaction Controller destructor " << endl; } |
The server message and event handlers are then declared and defined. MySRVMessageHandler derives from RTRServerMessageHandler and MySRVEventHandler derives from RTRServerEventHandler. In this example, the RTRServerMessageHandler methods OnAccepted, OnPrepareTransaction and the RTRServerEventHandler method OnServerIsPrimary are overridden. Both handler classes also define constructors and destructors.
class MySRVMessageHandler: public RTRServerMessageHandler { public: MySRVMessageHandler(); ~MySRVMessageHandler(); rtr_status_t OnPrepareTransaction( RTRMessage *pmyMsg, RTRServerTransactionController *pTC); rtr_status_t OnAccepted( RTRMessage *pmyMsg, RTRServerTransactionController *pTC); private: }; MySRVMessageHandler::MySRVMessageHandler() { } MySRVMessageHandler::~MySRVMessageHandler() { } rtr_status_t MySRVMessageHandler::OnPrepareTransaction( RTRMessage *pmyMsg, RTRServerTransactionController *pTC) { cout << "prepare txn " << endl; pTC->AcceptTransaction(); return RTR_STS_OK; } rtr_status_t MySRVMessageHandler::OnAccepted( RTRMessage *pmyMsg, RTRServerTransactionController *pTC) { cout << "accepted txn " << endl; pTC->AcknowledgeTransactionOutcome(); return RTR_STS_OK; } class MySRVEventHandler: public RTRServerEventHandler { public: MySRVEventHandler(); ~MySRVEventHandler(); rtr_status_t OnServerIsPrimary( RTREvent *pRTREvent, RTRServerTransactionController *pTC ); }; MySRVEventHandler::MySRVEventHandler() { } MySRVEventHandler::~MySRVEventHandler() { } MySRVEventHandler::OnServerIsPrimary( RTREvent *pRTREvent, RTRServerTransactionController *pTC ) { cout << "This server is primary " <<endl; return RTR_STS_OK; } |
In the server.cpp file, after including the server.h file and instantiating the SRVTransactionController class (myTC), the management class steps for setting up the RTR infrastructure take place. These steps create the RTR environment for client and server transactional messaging. This includes:
#include "srv.h" int main(void) { rtr_status_t sStatus; SRVTransactionController myTC; // start rtr RTR myRTR; sStatus = myRTR.Start(); cout << myRTR.GetErrorText(sStatus) << endl; // create journal sStatus = myRTR.CreateJournal(true); cout << myRTR.GetErrorText(sStatus) << endl; // create facility RTRFacilityManager myFac; // get nodes names for facility char *pszBackendNodes = "dejavu"; char *pszRouterNodes = "dejavu"; char *pszFrontendNodes = "dejavu"; char *nodename = "dejavu"; sStatus = myFac.CreateFacility(ABCFacility,pszRouterNodes, pszFrontendNodes,pszBackendNodes,false,false); cout << myRTR.GetErrorText(sStatus) << endl; RTRPartitionManager myPartition; char *low="A"; char *high="Z"; RTRKeySegment mySegment(rtr_keyseg_string,1, 0,low,high); sStatus = myPartition.CreateBackendPartition(ABCPartition, ABCFacility,mySegment,false,true,true); cout << myRTR.GetErrorText(sStatus) << endl; |
Then register the facility, partition and handler classes and instantiate a pointer to a data object (*myData).
sStatus = myTC.RegisterFacility(ABCFacility); cout << myRTR.GetErrorText(sStatus) << endl; sStatus = myTC.RegisterPartition(ABCPartition); cout << myRTR.GetErrorText(sStatus) << endl; MySRVMessageHandler myHandler; MySRVEventHandler myEventHandler; myTC.RegisterHandlers(&myHandler,&myEventHandler); RTRData *myData; |
Finally, create control loop logic with the Receive and Dispatch methods.
while(true) { sStatus = myTC.Receive(&myData); cout << "message received " << myRTR.GetErrorText(sStatus) << endl; if ( sStatus != RTR_STS_OK) { assert(false); } sStatus = myData->Dispatch(); cout << myRTR.GetErrorText(sStatus) << endl; delete myData; } cout << "hey I am done" <<endl; return 0; } |
This section uses the sample application included in the RTR kit as an example of implementing both a client and a server application using the C++ foundation classes.
The sample application is a simple client and server for ordering books and magazines.
The client takes orders and creates the corresponding Book or Magazine object. This object is told to serialize itself (write its state to a stream) and the client then sends the serialized object to a server.
The server application creates and registers two partitions. These partitions represent orders with ISBN numbers from 1-99 and 100-199. The server will register a custom class factory to peek at the object, which it is about to receive and determine its type, book or magazine. When the object has been created by the class factory and returned to the application the server will tell the object to deserialize itself and then to process itself. Processing means to carry out the business logic of buying the book or magazine.
The sample application demonstrates the following features:
In this sample there are three server classes and one client class. Each class is declared in its own .h file and implemented in a .cpp file.
The server classes are:
The client classes are:
There are three common data classes:
Figure 2-1 illustrates the messaging between the sample client and server applications.
Figure 2-1 Sample Application Messaging
This section provides examples of creating derived classes in the book- ordering sample application for implementing additional functionality in client and server application code by:
You can add functionality to an RTRData object without changing any code in the Message or Event handlers or the Receive loop, by deriving a class from RTRData.
Figure 2-2 illustrates the base class relationships to the ABCOrder data class. This class adds functionality to the RTRApplicationMessage class by defining three additional methods.
Figure 2-2 Adding Functionality to RTRData
For example, a book is represented as an ABCBook object with its
inherited Dispatch method from ABCOrder. This class overrides the
WriteObject, ReadObject, and Process methods. A magazine is represented
as an ABCMagazine object with overridden WriteObject ReadObject, and
Process methods, and the Dispatch method inherited from ABCOrder.
2.3.3 Encapsulating Data with RTRData
The following example illustrates the protocol class that encapsulates application-level data with an RTRData-derived class. In this sample application, two kinds of orders are processed by the server application, book orders and magazine orders. An order is defined as an ABCOrder object which derives from RTRApplicationMessage. All data sent between the client and server applications represents either a magazine order or a book order. As Figure 2-3 shows, there are two kinds or orders, book orders and magazine orders. This information is represented in a buffer organized for sending to the server from the client.
Figure 2-3 Encapsulating Data with RTRData
These two classes have been derived from the application's base class, ABCOrder. Book and Magazines are kinds of Orders. The order class tells its derived classes when to serialize their data. When this happens, the data in stored in the RTRData class via the methods of the RTRStream class.
When the client application is to make a request, the user enters the data for the fields illustrated above. The client application then stores this information in the corresponding book or magazine object and sends it to the server using SendOrder. The server then calls Receive to obtain the Book or Magazine order. Note that a Book (or magazine) is an RTRApplicationMessage.
In addition to RTRApplicationMessage data objects, three other kinds of RTR data can exist in the RTR application:
The application must be set up to handle these data classes, even if an application chooses to ignore them. In the sample application, if an order is an RTRApplicationMessage, then the object (an order) is processed by the Dispatch method. If the data is an RTRMessage or an RTREvent, then default handling occurs, and the event and message handler methods are called. The default Dispatch methods then execute, as each RTRData-derived data class has its own Dispatch method.
When ABCOrderProcessor calls its derived Receive method, one of the four types of data objects is assigned. The server can receive RTRMessage and RTREvent or can overwrite code in the class factory class to receive book or magazine orders. The class factory returns a pointer to incoming data (as an RTRData pointer) and knows what kind of object to return.
Previous | Next | Contents | Index |