# hp Reliable Transaction Router
C++ Foundation Classes

Previous | Contents | Index

Transaction Rejected

The rtr_mt_rejected message is from RTR, telling the server application that a participant in the transaction voted to reject it. If one participant rejects the transaction, it fails for all. The transaction will only be successful if all participants vote to accept it. When it receives this message, the server application should take this opportunity to roll back the current transaction if it is processing database transactions.

The sample server application includes the following code to check the order if it was not processed. If the order was not processed properly, then the handler method OnABCOrderNotProcessed is called and m_bVoteToAccept is set to false. This causes OnPrepareTransaction to reject the transaction.

        void ABCOrderProcessor::CheckOrderStatus (abc_sStatus sStatus)
        {
        // Check to see if there were any problems
        // processing the order. If so, let the handler know
        // to reject this txn when asked to vote.
            if (sStatus == ABC_STS_ORDERNOTPROCESSED)
            {
        // Let the handler know that the current txn should be rejected
            GetHandler()->OnABCOrderNotProcessed();
            };
        };
        void ABCSHandlers::OnABCOrderNotProcessed()
        {
            m_bVoteToAccept = false;
            return;
        }

Finally, explicitly end the transaction on a reject, the handler method OnRejected is called:

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

Transaction Accepted

RTR is telling the server that all participants in this transaction have voted to accept it. If database transactions are being done by the server, this is the place at which the server will want to commit the transaction to the database, and release any locks it may have taken on the database.

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

Note the AcknowledgeTransactionOutcome call in the server. This is an explicit method for completing a transaction.

That's it. You now know how to write a client and server application using RTR as your network communications, availability and reliability infrastructure. Congratulations!

Build and Run the Servers

Compile the ABC server and ABC shared files on the operating system that will run your server applications. If you are using two different operating systems, then compile it on each of them. To build on UNIX, issue the command:

        cxx -o server server.c shared.c /usr/shlib/librtr.so -DUNIX

You should start the servers before you start your clients. They will register with the RTR router so that the router will know where to send client requests. Start your primary server with the appropriate `run' command for its operating system along with the two parameters `1' and `h'. To run on UNIX:

        % ./server 1 h

Start your standby server with the parameters `2' and `h'.

        % ./server 2 h

Build and Run the Client: Compile the ABC CLIENT and ABC SHARED modules on the operating system which will run your client application. To build on UNIX:

        % cxx -o client client.c shared.c /usr/shlib/librtr.so -DUNIX

Run the client with the following command:

        % ./client 1 h 10

or

        C:\RtrTutor\> client.exe 1 h 10

In many ways, this tutorial has only scratched the surface of RTR. There is a great deal more that RTR gives you to make your distributed application reliable, available, and perform better. The following sections of this document highlight some of the capabilities you have at your service. For more details on each item, and information on what additional features will help you to enhance your application, look first through the RTR Application Design Guide. Then, earlier sections of this C++ Foundation Classes manual will tell you in detail how to implement each capability.

Compaq Computer Corporation also offers training classes for RTR, and if you'd like to attend any of them, contact your Compaq representative.

Callout Server

RTR supports the concept of a "callout server" for authentication. You may designate an additional application on your server machines or your router machine as a callout server with the RTRFacilityManager class methods. Callout servers will be asked to check all requests in a facility, and are asked to vote on every transaction.

The CreateFacility method in the RTRFacilityManager class includes a boolean parameter bEnableBackendCallout for specifying a callout server.

Events

In addition to messages, RTR can be used to dispatch asynchronous events on servers and clients. A callback function in the user's server and client applications can be designated which RTR will call asynchronously to dispatch events to your application.

Shadowing

This tutorial only discussed failover to a standby server. But RTR also supports shadowing: while your server is making changes to your database, another "shadow" server can be making changes to an exact copy of your database in real time. If your primary server fails, your shadow server will take over, and record all of the transactions occurring while your primary server is down. Your primary server will be given the opportunity to update the original database and catch up to the correct state when it comes back up. So as you can see, if your database and transactions are important enough to you, you have the opportunity to double protect them with an RTR configuration including any of

- Multiple standby software servers on a primary hardware backend system
- Shadow backend system replicating all transactions on a duplicate database
- Failover backend systems for each of your primary and shadow backends
- Failover routers

Transactions

One of RTR's greatest strengths is in supporting transactions. The RTR Application Design Guide goes into more detail regarding transactions and processing of transactions.

RTR Utility

You've seen how to use the RTR utility (or the command line interface) to start RTR and to create a facility. But the RTR utility contains many more features than this, and in fact can be used to prototype an application. Refer to the RTR System Manager's Manual for details.

# Chapter 6
Sample Application Code

The RTR book ordering sample application shows how the C++ Foundation classes can be used to simulate purchasing merchandise for a fictitious company named ABC.

The client, ABCOrderTaker, has a hard-coded book request which is represented by the ABCBook class. This book request has an ISBN number used for data routing. The server will display a dialog box containing the contents of the newly reconstituted ABCBook object.

The following sample application code comes from the Examples directory and includes:

- Sample1
  This file contains `int main` and provides a sample for which the sample application takes book and magazine orders from the client application (ABCOrderTaker) and processes them in the server application (ABCOrderProcessor).
- ABCOrderTaker
  Located in ABCOrderTaker.h and ABCOrderTaker.cpp, this client-side class supplies ability to pass an object derived from ABCOrder to a server. This class is derived from RTRClientTransactionController and derives from RTRClientMessageHandler and RTRClientEventHandler
- ABCOrderProcessor
  Located in ABCOrderProcessor.h, ABCOrderProcessor.cpp, this server-side class processes the request sent to it by the client. This class is derived from RTRServerTransactionController.
- ABCOrder
  Located in ABCOrder.h, ABCOrder.cpp, this is an abstract base class (for ABCBook and ABCMagazine) which requires all derived classes to implement three member functions.

  > ReadObject()
  > WriteObject()
  > Process()

  This class is derived from RTRApplicationMessage.
- ABCBook
  Located in ABCBook.h, ABCBook.cpp, this class represents a book order. This class is able to write and read its state to the memory managed by its base class RTRData. This class derives from ABCOrder.

There are also class factory, client and server handlers, ABCMagazine, and ABCCommon classes in the Examples directory.

# 6.1 Sample Main Program

        #include "ABCCommon.h"
        #include "ABCOrderTaker.h"
        #include "ABCOrderProcessor.h"
        #include "ABCBook.h"
        #include "ABCMagazine.h"
        void GenerateOrders();

        int main(int argc, char* argv[])
        {
        bool bValidInput = false;
        while (false == bValidInput)
        {
        cout << endl;
        cout << "**********************************************" << endl;
        cout << endl;
        cout << "1 - Start Server to process incoming orders" << endl;
        cout << "2 - Start Client to generate predefined orders" << endl;
        cout << "0 - Quit" << endl;
        cout << endl;
        cout << "**********************************************" << endl;
        cout << endl << "Which Test should be run? : ";
        unsigned int uiAnswer;
        cin >> uiAnswer;
        switch (uiAnswer)
            {
            case 1 : {  ABCOrderProcessor OrderProcessor;
                // Call ProcessIncomingOrders which will loop
                //forever processing orders from clients.
                OrderProcessor.ProcessIncomingOrders();
                break;
            }
            case 2 : {
                // Send some orders
                GenerateOrders();
                break;
            }
            case 0 : {
                return 0;
            }
            } // switch
              } //while
            return 0;
        }

        void GenerateOrders()
        {
            abc_status sStatus;
            // Create an Order Taker.
            ABCOrderTaker OrderTaker;

        // Create a sample book order and populate it with the
        // ISBN 49, Price and Title
        ABCBook Book;
        Book.AddOrder( 49, 12345, "Everything to the Internet",
                                            "Michael Capellas");
        // Send this book order to the server for processing.
        // note: This will be txn #1
            cout << endl << "Transaction # 1" <<endl;
            sStatus = OrderTaker.SendOrder(&Book);
            cout << endl;
        // Reset the stream. This way we will reuse the beginning of
        // the buffer that the stream manages.
        Book.ResetStream();

            // Send another order to a server which handles ISBN 99
        Book.AddOrder( 99, 56789, "Java How To Program",
                                          "Deitel & Deitel");
     // Send this book order to the server for processing.
            // note: This will be txn #2
            cout << endl << "Transaction # 2" <<endl;
            sStatus = OrderTaker.SendOrder(&Book);
            cout << endl;

            ABCMagazine Magazine;

        Magazine.AddOrder(29,"PC Week","ZIFF-DAVIS", "February 2000");

            // Send this book order to the server for processing.
            // note: This will be txn #3
            cout << endl << "Transaction # 3" <<endl;
            sStatus = OrderTaker.SendOrder(&Magazine);
            cout << endl;
        }

# 6.2 Client Application ABCOrderTaker

        // ABCOrderTaker.cpp: implementation of the ABCOrderTaker class.
        //
        //////////////////////////////////////////////////////////////////////

        #include "ABCCommon.h"
        #include "ABCOrderTaker.h"

        //////////////////////////////////////////////////////////////////////
        // Construction/Destruction
        //////////////////////////////////////////////////////////////////////

        ABCOrderTaker::ABCOrderTaker() : m_bRegistered(false)
        {

        }

        ABCOrderTaker::~ABCOrderTaker()
        {

        }

        abc_status ABCOrderTaker::SendOrder(ABCOrder *pOrder)
        {
            abc_status sStatus;
        //  Register with RTR if we havn't already done so.
        //  This will make sure we are ready to start sending data.
            sStatus = Register();
            if (ABCSuccess != sStatus) return false;
        // If we can't register with RTR then exit
        // Start the Transaction
            cout << "StartTransaction..." << endl;
            sStatus = StartTransaction();
            print_status_on_failure(sStatus);
        //   Send this Book Order object to a server capable
        //   of processing it.
            cout << "SendApplicationMessage..." << endl;
     sStatus = SendApplicationMessage(pOrder);
     print_status_on_failure(sStatus);

        //   Let RTR know that this is the only object being sent
        //   and that we are done with our work.
            cout << "AcceptTransaction..." << endl;
            sStatus = AcceptTransaction();
            print_status_on_failure(sStatus);
     //   Determine if the server successfully processed the request
                return DetermineOutcome();
            }
        rtr_status_t ABCOrderTaker::Register()
        {
            rtr_status_t sStatus = RTR_STS_OK;
        if(false == m_bRegistered)
            {
        //   If RTR is not already started then start it now.
                sStatus = StartRTR();
        //   Create a Facility if not already created.
                sStatus = CreateFacility();

        //   Register our facility with RTR.
                sStatus = RegisterFacility(ABCFacility);
                print_status_on_failure(sStatus);
        if(RTR_STS_OK == sStatus)
                {
                    m_bRegistered = true;
                }

        // ABC Handlers
            sStatus = RegisterHandlers(&m_rtrHandlers,&m_rtrHandlers);
            print_status_on_failure(sStatus);
            }
            return sStatus;
        }
        abc_status ABCOrderTaker::DetermineOutcome()
        {
            RTRData  *pResult = NULL;
            abc_status sStatus = ABCSuccess;

            // Simply wait for RTR to send us an accepted or rejected.

            // We can dispatch everything we get and let the default
            // handlers process what we don't care about.

            bool bDone = false;
            while (!bDone)
            {
            sStatus = Receive(&pResult);
            print_status_on_failure(sStatus);
            sStatus = pResult->Dispatch();
            if (ABCOrderSucceeded == sStatus)
            {
            cout << "Transaction succeeded..." << endl;
            bDone = true;
            }
            else
                if (ABCOrderFailed == sStatus)
                {
                cout << "Transaction failed..." << endl;
                bDone = true;
                }
            }
        delete pResult;
            return sStatus;
        }

# 6.3 Server Application ABCOrderProcessor

        // ABCOrderProcessor.cpp: implementation of the ABCOrderProcessor class.
        //
        //////////////////////////////////////////////////////////////////////

        #include "ABCCommon.h"
        #include "ABCOrderProcessor.h"
        #include <stdio.h>

        //////////////////////////////////////////////////////////////////////
        // Construction/Destruction
        //////////////////////////////////////////////////////////////////////

        ABCOrderProcessor::ABCOrderProcessor()
        {

        }

        ABCOrderProcessor::~ABCOrderProcessor()
        {

        }

        void ABCOrderProcessor::ProcessIncomingOrders()
        {
        //   Register with RTR. This will make sure we are ready to
        //   start receiving data.
            Register();
        //   Start processing orders
            abc_status sStatus = RTR_STS_OK;
            RTRData *pOrder = NULL;

            while (1)
            {
        //   Receive an Order
            sStatus = Receive(&pOrder);
            print_status_on_failure(sStatus);
            if(ABCSuccess != sStatus) break;
            // if we can't get an Order then stop processing.

        // Dispatch the Order to be processed
        // note: This could be any kind of data. ie. RTRMessage RTREvent,
        // RTRApplicationMessage or RTRApplicationEvent.
        // The class ABCOrder(derived from RTRApplicationMessage) has
        // redefined the Dispatch() method to call the Process() method
        // of its derived class (ABCBook or ABCMagazine). All other
        // data classes use the default implemenation of Dispatch()
        // which will call the appropriate handler.
                sStatus = pOrder->Dispatch();
                print_status_on_failure(sStatus);
        // Check to see if there were any problems processing the order.
        // If so, let the handler know to reject this txn when asked to
        // vote.
        // note : For the ABC company, orders are processed in the
        // Process() method of all ABCOrder derived classed.
                CheckOrderStatus(sStatus);

        //   Delete this order that was allocated by the class factory.
        // note: In this sample the class factory returns a separate
        // instance of an order each time it is called.
                delete pOrder;
            }
        return;
     }
     void ABCOrderProcessor::Register()
        {
            rtr_status_t sStatus;

            // Create an environment that our server can run in.
            CreateRTREnvironment();

            // Register with RTR the following objects
            sStatus = RegisterFacility(ABCFacility);
            print_status_on_failure(sStatus);

            // ABC Partition
            sStatus = RegisterPartition(ABCPartition1);
            print_status_on_failure(sStatus);

            sStatus = RegisterPartition(ABCPartition2);
            print_status_on_failure(sStatus);

            // ABC Class Factory
            sStatus = RegisterClassFactory(&m_ClassFactory);
            print_status_on_failure(sStatus);

            // ABC Server Handlers
            sStatus = RegisterHandlers(&m_rtrHandlers,&m_rtrHandlers);
            print_status_on_failure(sStatus);

            return;
        }

        void ABCOrderProcessor::CreateRTREnvironment()
        {
            rtr_status_t sStatus;
            // If RTR is not already started then start it now.
            StartRTR();
            // Create a Facility if not already created.
            CreateFacility();
            // 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);

            // Create a partition that processes ISBN numbers in the
            // range 100 - 199
            low = 100;
            max = 199;
            RTRKeySegment Key100To199(  rtr_keyseg_unsigned,
                                        sizeof(int),
                                             0,
                                             &low,
                                             &max );
            sStatus = PartitionManager.CreateBackendPartition(
                                                       ABCPartition2,
                                                       ABCFacility,
                                                       Key100To199,
                                                       false,
                                                       true,
                                                       false);
            print_status_on_failure(sStatus);
        }

        void ABCOrderProcessor::CheckOrderStatus (abc_status sStatus)
        {
        // Check to see if there were any problems processing the order.
        // If so, let the handler know to reject this transaction when
        // asked to vote.
            if (sStatus == ABCOrderFailed)
            {
        // Let the handler know that the current txn should be rejected
                GetHandler()->OnABCOrderNotProcessed();
            };

        }

# 6.4 Data Class ABCOrder

        // ABCOrder.cpp: implementation of the ABCOrder class.
        //
        //////////////////////////////////////////////////////////////////////

        #include "ABCCommon.h"
        #include "ABCOrder.h"

        //////////////////////////////////////////////////////////////////////
        // Construction/Destruction
        //////////////////////////////////////////////////////////////////////

        ABCOrder::ABCOrder() : m_uiPrice(0)
        {
            m_szTitle[0] = '\0';
            m_szAuthor[0] = '\0';
        }

        ABCOrder::~ABCOrder()
        {

        }

        rtr_status_t ABCOrder::Dispatch()
        {
            // Populate the derived object
            ReadObject();
        // Since we have overridden Dispatch() in our base class
        // (RTRApplictaionMessage), the handler will not be called
        // unless we do it ourselves. If we call our base class Dispatch
        // method the handler methods OnInitialize() and
        // OnApplictionMessage() will be called. This sample uses
        // OnInitialize() to print out notification that a new
        // transaction
        // is starting.
            RTRApplicationMessage::Dispatch();
        // Process the purchase which the derived object represents
            abc_status status = ProcessOrder();
        return status;
        }

# 6.5 Data Class ABCBook

        // ABCBook.cpp: implementation of the ABCBook class.
        //
        //////////////////////////////////////////////////////////////////////

        #include "ABCCommon.h"
        #include "ABCBook.h"

        //////////////////////////////////////////////////////////////////////
        // Construction/Destruction
        //////////////////////////////////////////////////////////////////////

        ABCBook::ABCBook() : m_uiISBN(0)
        {

        }

        ABCBook::~ABCBook()
        {

        }

        bool ABCBook::AddOrder( unsigned int uiPrice,
                                unsigned int uiISBN,
                                char *pszName,
                                char *pszAuthor)
        {
        //  Copy the Book purchase to our Book object.
            m_uiISBN = uiISBN;
            m_uiPrice = uiPrice;
            strcpy(&m_szTitle[0],pszName);
            strcpy(&m_szAuthor[0],pszAuthor);
            WriteObject();
            return true;
        }
     void ABCBook::WriteObject()
     {
     // Save the type of object we are. This is used by the
     // class factory on the server side to determine which type
     // of class to allocate.
     *this << ABC_BOOK;
     *this << m_uiPrice << m_uiISBN  << m_szTitle << m_szAuthor;
     // The 1 line call above is equivalent to the 4 lines below. We
     // can use the << and >> operators because we know that the data
        // which we store is not > the current RTR maximum  = 65535 byes.
        // WriteToStream(m_uiISBN);
        // WriteToStream(m_uiPrice);
        // WriteToStream(m_szTitle);
        // WriteToStream(m_szAuthor);
            char mystring[] = "ABCDEFGHIJKLMNOPQRSTUVWZYZ";
            rtr_msgbuf_t p = &mystring[0];
            rtr_msglen_t length = strlen(mystring)+1;
            WriteToStream(p,length);
        }
        void ABCBook::ReadObject()
        {
        // The first data is the type of class we should be.
        // Validate that everything is fine.
            unsigned int uiClassType = 0;
            *this >> uiClassType;
            assert(uiClassType == ABC_BOOK);
        //  Populate this object with the data
            *this >> m_uiPrice >> m_uiISBN  >> m_szTitle >> m_szAuthor;
        // The 1 line call above is equivilant to the 4 lines below.
        // ReadFromStream(m_uiISBN);
        // ReadFromStream(m_uiPrice);
        // ReadFromStream(m_szTitle,GetLogicalBufferLength());
        // ReadFromStream(m_szAuthor,GetLogicalBufferLength());
        }
        abc_status ABCBook::ProcessOrder()
        {
            // It is here that we would process the request for this book.
            // For this sample simply print out the Book order.
        cout <<"ABCBook::ProcessOrder()" << endl;
            cout << "     " << "ISBN = " << m_uiISBN << endl;
            cout << "     " << "Price = " << m_uiPrice << endl;
            cout << "     " << "Title = " << m_szTitle << endl;
            cout << "     " << "Author = " << m_szAuthor << endl;

            return ABCOrderSucceeded;
        }

}

Previous | Next | Contents | Index