# hp Reliable Transaction Router
C++ Foundation Classes

Previous | Contents | Index

# Chapter 2
Design and Implementation

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:

- Design steps
- Implementation steps
- Implementation example
- Derived receive models
- Sample application walkthrough

# 2.1 Design Steps

When creating a new client or server application:

1. Analyze your application requirements.
   Consider your business functions and map them to C++ classes. In the sample application, the client application accepts orders to purchase books, and the server application processes these orders from the client application.
2. Define your data protocol.
   The data protocol defines the data that is passed between client and server applications. In the sample application, orders are passed between client and server. These orders can be books or magazines. Book is a type of Order.
3. Determine if your application should use the default Message and Event handlers.
   A properly designed RTR application must handle all the possible messages and events that it may receive. To make this task easier Handler classes are provided, RTRServerMessageHandler and RTRServerEventHandler. These two classes provide a separate method for each potential message and event that an application may receive. The methods provide a default implementation for the application.
   Most applications will benefit from using the default handlers. Using these handlers simplifies your design by allowing you to derive your own handlers from the default handlers and override only the messages and events which are of interest to your application. The messages and events, which are not overridden, are processed using the default implementation supplied in the base class.
   Review: The Receive() method on a Transaction Controller returns an object derived from RTRData. This may be a Message or Event sent by the application or RTR itself. To process this unknown message or event the application simply needs to call the Dispatch() method on the RTRData derived object.
   In rare situations an application may decide that it does not wish to use handlers. Unless a handler is registered with the Transaction controller it will not be used. In this case calling Dispatch would return an error.
4. Determine if your application should derive from RTRClassFactory.
   When the Receive() method of a Transaction Controller is called RTR needs to create an object derived from RTRData to hold the data being received. More specifically, it creates one of the following objects:
   - RTRApplicationMessage
   - RTRApplicationEvent
   - RTRMessage
   - RTREvent
   An application may wish to have its own object returned when Receive is called. This is easily achieved by registering its own class factory object, which is derived from RTRClassFactory. RTR will call the appropriate method in the class factory and the application may return its own class, which is derived from the base class being created. This allows the application great flexibility when processing incoming data.
   Many applications will find it very valuable to derive their own class(es) from RTRApplicationMessage and return instances of this class from their custom class factory.
   RTR calls the CreateRTRApplicationMessage() method of the class factory with the data being received. This allows the application to parse the data before it is received and return the correct object for the application. For example, the sample application looks at the application message being received, determines if it is receiving a book or magazine and returns an instance of the correct object.
   In some circumstances an application may always pass only one type of data, in this case it may chose not to register a class factory.

# 2.2 Implementation Steps

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:

- Create an environment for the application to run. This includes starting RTR, creating a facility, defining one or more key segments and creating one or more partitions.
- Create an RTRServerTransactionController within your server code.
- Register a facility by name.
- Register a partition by name.
- Register a class derived from RTRClassFactory [optional].
- Register a class derived from RTRServerMessageHandler [optional].
- Register a class derived from RTRServerEventHandler [optional].
- Create the control loop that includes receive and dispatch methods.
- Accept the transaction when your business logic succeeds.
- Acknowledge the outcome of the transaction.

For example, the typical steps for implementing a server are the following:

1. Create an environment for the application to run by registering a partition for the server.
   - Create an RTRKeySegment class. For example, the following sample creates an RTRKeySegment for all ASCII values between "A" and "z":

         // 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 );

   - Create an RTR partition with the above KeySegment. The following example includes constants for the names of the partition and facility.

         RTRPartitionManager PartitionManager;
         sStatus = PartitionManager.CreateBackendPartition( ABCPartition1,
         ABCFacility,
         KeyZeroTo99,false,true,false);
         print_status_on_failure(sStatus);

     While the above example shows only the RTR_STS_OK return value, typical applications must check for other status returns.
2. Instantiate the RTRServerEventHandler and RTRServerMessageHandler classes.

           SimpleServerEventHandler *pEventHandler = new
                                   SimpleServerEventHandler();
           SimpleServerMessageHandler *pMessageHandler = new
                                   SimpleServerMessageHandler();

3. Create an RTRServerTransactionController to receive incoming messages and events from a client.

           RTRServerTransactionController *pTransaction = new
                                    RTRServerTransactionController();

4. Register the facility, partition and both handlers with the transaction controller.

           ....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);

5. Create a RTRData pointer. This pointer is assigned a pointer to a message or event when RTRSoerverTransactionController::Receive is called.

           RTRData *pDataReceived = NULL;

6. Create a control loop to continually receive messages and dispatch them to the message and event handlers.

       while (true)
           {
           sStatus = pTransaction->Receive(pDataReceived);
           print_status_on_failure(sStatus);
           sStatus = pDataReceived->Dispatch();
           print_status_on_failure(sStatus);
           }

7. Accept the Transaction when your business logic succeeds. When the server has successfully finished its work, tell RTR that it is willing to accept the transaction.

           RTRServerTransactionController * pController;
           pController->AcceptTransaction();

   Note the default behavior supplied by the OnPrepareTransaction method of the RTRServerMessage handler is to call AcceptTransaction on behalf of the application.
   The sample application overrides this default behavior to reject the transaction if the order could not be processed.

           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();
               }

8. Acknowledge the outcome of the transaction. A server must tell RTR that it has received the outcome of the transaction. This explicitly tells RTR that it is ok for this Transaction Controller to process the next transaction.
   The default behavior in RTRServerMessageHandler::OnAccepted is to acknowledge the outcome of the transaction.

           void ABCSHandlers::OnAccepted( RTRMessage *pRTRMessage,
           RTRServerTransactionController *pController )
           {    pController->AcknowledgeTransactionOutcome();
                return;
           }

## 2.2.2 Implementing a Client

To implement a client application, you:

- Create an RTRClientTransactionController.
- Register a facility
- Send a message
- Accept the transaction

In more detail:

1. Create a ClientTransactionController to receive the incoming messages and events from a server.

       RTRClientTransactionController *pTransaction = new
                                       RTRClientTransactionController();

2. Register the facility with the TransactionController object.

       sStatus = RegisterFacility(ABCFacility);
               print_status_on_failure(sStatus);
       if(RTR_STS_OK == sStatus)
               {
                  m_bRegistered = true;
               }

3. Create an RTRApplicationMessage derived class that adds to the Data object the information that is to be sent to the server. Usually the data is added within the derived class by calling RTRStream::WriteToStream.

           class MyApplicationMessage : public RTRApplicationMessage
           MyApplicationMessage *pMessage1 = new  MyApplicationMessage()

4. Send a message to the server.

           sStatus = pTransaction->SendApplicationMessage(pMessage1);
           print_status_on_failure(sStatus);

5. Accept the transaction. When the application has successfully finished the transaction, the client tells RTR that it votes to accept the transaction.

           pTransaction->AcceptTransaction();

   The client application's business logic operates between sending a first message to the server and accepting the transaction (Step 5).

## 2.2.3 Implementation Example

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:

- Starting RTR (myRTR.Start)
- Creating a journal (myRTR.CreateJournal(true))
- Creating a facility (myFac)
- Defining a partition (myPartition)
- Defining a key segment (mySegment)
- Creating a server partition (myPartition.CreateBackendPartition)

          #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;
        }

# 2.3 Sample Application Walkthrough

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:

- Serializing and Deserializing an application-defined object with RTR.
- Creating multiple partitions, each with a different key segment, including ABCPartition1 and ABCPartition2.
- Using default handlers for RTR messages, for example, default calls to methods such as OnAccepted and OnRejected.
- Using default handlers for RTR events, for example, default calls to methods such as OnServerIsPrimary.
- Dispatching RTRData-derived objects, for example, pOrder->Dispatch().

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:

- ABCOrderProcessor: this class derives from
  RTRServerTransactionController.
- ABCSClassFactory: this class derives from RTRClassFactory.
- ABCSHandlers: this class derives from RTRServerEventHandler and RTRServerMessageHandler.

The client classes are:

- ABCOrderTaker: this class derives from RTRClientTransactionController.
- ABCCHandlers this class derives from RTRClientEventHandler and RTRClientMessageHandler.

There are three common data classes:

- ABCOrder: this class derives from RTRApplicationMessage.
- ABCBook: this class derives from ABCOrder.
- ABCMagazine: this class derives from ABCOrder.

Figure 2-1 illustrates the messaging between the sample client and server applications.

Figure 2-1 Sample Application Messaging

## 2.3.1 Deriving from Base Classes in the Sample Application

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:

- Adding functionality to RTRData-derived data objects
- Encapsulating data
- Examining RTRData objects

## 2.3.2 Adding Functionality to Data Objects

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:

- RTRApplicationEvent
- RTRMessage
- RTREvent

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