This chapter provides brief information on getting started with writing C++ applications to connect with FioranoMQ Server. Following sections detail on the C++ RTL namespace, error handling, handling exception listeners, message listeners, and advisory message listeners

Destinations

 

A destination is the object a client uses to specify the target of messages it produces and the source of messages it consumes. In the PTP messaging domain, destinations are called Queues. In the pub/sub messaging domain, destinations are called Topics.

Temporary Destinations

 

An application typically uses a Temporary Destination to receive replies to request messages. To specify the destination where a reply to a request message is to be sent, an application calls the Set JMSReplyTo method of the Message object representing the request message. The destination specified on the call can be a temporary destination.

To create a temporary queue, a C application calls the TS_createTemporaryQueue(QueueSession) function and TS_createTemporaryTopic(TopicSession) for creating a Topic, with session as the parameter. In a C++ application, a temporary queue is created by calling createTemporaryQueue method of Session object and createTemporaryTopic for a Topic destination.

Message Producers

 

A message producer is an object created by a session and is used for sending messages to a destination. The PTP form of a message producer uses the CQueueSender class. The pub/sub form uses the CTopicPublisher class.

You can create an unidentified producer by specifying NULL as the argument to createSender or createPublisher. With an unidentified producer, you can wait to specify which destination to send the message to until you send or publish a message. If you created an unidentified producer, use the overloaded send or publish method that specifies the destination as the first parameter.

Message Consumers

 

A message consumer is an object created by a session and is used for receiving messages sent to a destination. A message consumer allows a JMS client to register interest in a destination with a JMS provider. The JMS provider manages the delivery of messages from a destination to the registered consumers of the destination. The PTP form of message consumer uses the CQueueReceiver class. The pub/sub form uses the CTopicSubscriber class.

When using either the CQueueReceiver or the CTopicSubscriber, you can use the receive method to consume a message synchronously. Use this method at any time after you call the start method.

For consuming the messages asynchronously, message listeners are provided.

Namespaces in C++

All the C++ classes are declared in a namespace called cppnativertl.  A C++ application can therefore adopt one of the following approaches when referring to the names of cppnativertl classes:

The application can qualify the names of cppnativertl classes with the name of the namespace, cppnativertl, as show in the following C++ code fragment:

#include <cppHeaders.h>
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
      cppnativertl :: CHashTable *m_env;
      cppnativertl :: CInitialContext *m_ic;
       // Queue Connection Factory
       cppnativertl :: CQueueConnectionFactory *m_qcf;
       // Queue Connection
       cppnativertl :: CQueueConnection *m_qc;

       // Set up the environment variables
       m_env = new CHashTable();
       m_env->Put(SECURITY_PRINCIPAL, "anonymous");
       m_env->Put(SECURITY_CREDENTIALS,"anonymous");
       m_env->Put(CONNECT_URL,"http://localhost:1856");
       m_env->Put(BACKUP_URLS,"http://localhost:1956");
       m_env->Put(PROVIDER_URL,"http://localhost:1856");

       m_ic = new CInitialContext(m_env);

       m_qcf = dynamic_cast<CQueueConnectionFactory *>(m_ic->Lookup("primaryQCF"));
       if(m_qcf == 0)
       {
              throw new CJMSException("Connection factory lookup failed.Error in Type casting.");
       }

       m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");

       //Other code here
       //Deletion of objects
       return(0);
}

The application can use a using directive to make the names of cppnativertl classes available without having to qualify them. For example:

#include <cppHeaders.h>
#include <iostream>
using namespace std;
// Using cpp native rtl namespace
using namespace cppnativertl;
int main(int argc,char* argv[])
{
      CHashTable *m_env;
      CInitialContext *m_ic;
      // Queue Connection Factory
      CQueueConnectionFactory *m_qcf;
      // Queue Connection
      CQueueConnection *m_qc;

      // Set up the environment variables
      m_env = new CHashTable();

      m_env->Put(SECURITY_PRINCIPAL, "anonymous");
      m_env->Put(SECURITY_CREDENTIALS,"anonymous");
      m_env->Put(CONNECT_URL,"http://localhost:1856");
      m_env->Put(BACKUP_URLS,"http://localhost:1956");
      m_env->Put(PROVIDER_URL,"http://localhost:1856");

      m_ic = new CInitialContext(m_env);

      m_qcf = dynamic_cast<CQueueConnectionFactory *>(m_ic->Lookup("primaryQCF"));
      if(m_qcf == 0)
      {
             throw new CJMSException("Connection factory lookup failed.Error in Type casting.");
      }

      m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");
      //Other code here
      //Deletion of objects
      return(0);
}

Error Handling

The C++ RTL uses exceptions to provide error handling. A C++ RTL exception is an object of one of the following types:

  • CFioranoException
  • CJMSException

The CFioranoException class is a super class of CJMSException which has a utility function checkForException() that throws CJMSException if an exception occurs. As a result, an application can include all the Fiorano C++ RTL methods in a try-catch block and to catch all types of exception. In event of an error in the C++RTL layer, CJMSException with a specific error code and description is thrown.

The Exception can be caught at the application level and the associated message can be read using the public API getMessage(). The exception handling is also exhaustive; it provides the complete function stack trace at the moment of the error.

The exception stack is maintained in the Thread-local storage of C RTL, so that the exception that occurred in one thread doesn't interfere with the flow of other threads. The stack trace can be printed on the console using the API call printStackTrace().

The following code fragment illustrates this technique:

#include <cppHeaders.h>
#include <iostream>
using namespace std;
// Using cpp native rtl namespace
using namespace cppnativertl;
int main(int argc,char* argv[])
{
     CHashTable *m_env;
     CInitialContext *m_ic;
     // Queue Connection Factory
     CQueueConnectionFactory *m_qcf;
     // Queue Connection
     CQueueConnection *m_qc;
     CQueue *m_queue;
     // Set up the environment variables
     try
     {
          m_env = new CHashTable();
          m_env->Put(SECURITY_PRINCIPAL, "anonymous");
          m_env->Put(SECURITY_CREDENTIALS,"anonymous");
          m_env->Put(CONNECT_URL,"http://localhost:1856");
          m_env->Put(BACKUP_URLS,"http://localhost:1956");
          m_env->Put(PROVIDER_URL,"http://localhost:1856");
          m_ic = new CInitialContext(m_env);
          m_qcf = dynamic_cast<CQueueConnectionFactory *> (m_ic->Lookup("primaryQCF"));
          if(m_qcf == 0)
          {
               throw new CJMSException("Connection Factory lookup failed.Error in Type casting.");
          }
          m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");
          //Additional code here
         //Deletion of objects
    }
    catch(CJMSException *e)
    {
        cout << e->getMessage();
        e->printStackTrace();
       delete e;
    }
    return(0);
}

The application must release the CJMSException object using the delete operator. The C++ RTL can create an exception for each error it detects during a call and link the exceptions to form a chain. After an application has caught the first exception, it can call the getLinkedException() method to get a pointer to the next exception in the chain. The application can continue to call the getLinkedException() method on each exception in the chain until a null pointer is returned, indicating that there are no more exceptions in the chain. Because the getLinkedException() method returns a pointer to a linked exception, the application must release the object using the delete operator.

Message listeners in C++

A C++ application uses a message listener to receive messages asynchronously. To receive messages asynchronously, the C++ application must define a message listener class that is based on the abstract class CMessageListener. The message listener class defined in the application must provide an implementation of the onMessage() method. The application can then instantiate the class to create a message listener and register the message listener with one or more message consumers by calling the setMessageListener() method for each message consumer. Subsequently, when a message arrives for a message consumer, the onMessage() method is invoked to deliver the message.

To stop the asynchronous delivery of messages to a message consumer, the application can call the setMessageListener() method again by passing a null pointer as a parameter instead of a pointer to a message listener. Unless the registration of a message listener is cancelled in this method, the message listener must exist for as long as the message consumer exists.

A new message listener can be registered with a message consumer without cancelling the registration of an existing message listener. If the onMessage() method of an existing message listener is running when a new message listener is registered, the active method completes normally and any subsequent messages are processed by calls to the onMessage() method of the new message listener. If a transaction is in progress when a message listener is changed, the transaction is completed by calls to the onMessage() method of the new message listener.

The following code fragment provides an example of a message listener class implementation with an onMessage() method:


#include <cppHeaders.h>
#include <iostream>
using namespace std;
// Using cpp native rtl namespace
using namespace cppnativertl;
// Message Listener implementation
class CMyMessageListener: public CMessageListener
{
     public:
              void onMessage(CMessage *msg)
              {
                    if(msg != NULL)
                    { 
                          CTextMessage *cmsg = (CTextMessage *)msg;
                          cout << "Received Message :: " << cmsg->getText() <<endl;
                          delete msg;
                    }
             }
};

As the C ++RTL delivers a pointer to a message when it calls the onMessage() method, the application must release the message using the delete operator.

The following code fragment shows how an application can use this message listener class to implement the asynchronous delivery of messages to a message consumer:

#include <cppHeaders.h>
#include <iostream>
using namespace std;

// Using cpp native rtl namespace
using namespace cppnativertl;

int main(int argc,char* argv[])
{
    CHashTable *m_env;
    CInitialContext *m_ic;
    // Queue Connection Factory
    CQueueConnectionFactory *m_qcf;
    // Queue Connection
    CQueueConnection *m_qc;
    CQueue *m_queue;
    CMessageListener *m_msgl;

    try
    {

         // Set up the environment variables
         m_env = new CHashTable();

         m_env->Put(SECURITY_PRINCIPAL, "anonymous");
         m_env->Put(SECURITY_CREDENTIALS,"anonymous");
         m_env->Put(CONNECT_URL,"http://localhost:1856");
         m_env->Put(BACKUP_URLS,"http://localhost:1956");
         m_env->Put(PROVIDER_URL,"http://localhost:1856");

         m_ic = new CInitialContext(m_env);

         m_qcf = dynamic_cast<CQueueConnectionFactory *>(m_ic->Lookup("primaryQCF"));
         if(m_qcf == 0)
         {
             throw new CJMSException("Connection Factory lookup failed.Error in Type casting.");
         }

         m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");

         m_queue = dynamic_cast<CQueue *>(m_ic->Lookup("primaryQueue"));
         if(m_queue == 0)
         {
              throw new CJMSException("Destination lookup failed.Error in Type casting.");
         }

         m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");
         m_qs = m_qc->createQueueSession(FALSE, AUTO_ACKNOWLEDGE);
         m_receiver = m_qs->createReceiver(m_queue);

         // Message Listener Object
         m_msgl = new CMyMessageListener();
         m_receiver->setMessageListener(m_msgl);
         m_qc->start();

         //Other code here
         //Deleting the objects
         delete m_env;
         if(m_receiver != NULL)
                m_receiver->close();
         delete m_receiver;
         if(m_qs != NULL)
                m_qs->close();
         delete m_qs;
         if(m_qc != NULL)
                m_qc->close();
         delete m_qc;
         delete m_qcf;
         delete m_queue;
         delete m_ic;
         delete m_msgl;
    }
    catch(CJMSException *e)
    {
          cout << e->getMessage();
           e->printStackTrace();
          delete e;
      }
      return(0);
}

 

Connection Start and Stop

When an application creates a connection, the connection is in stop mode. When the connection is in stop mode, the application can initialize sessions and it can send messages but cannot receive them, either synchronously or asynchronously.

An application can start a connection by calling the Start Connection method. When the connection is in start mode, the application can send and receive messages. The application can stop and restart the connection by calling the stop() and start() methods.

Connection Close

In the above code fragment, the statement m_qc->close() closes the connection. When an application closes a connection, it closes all the sessions associated with the connection and deletes certain objects associated with these sessions. It also rolls back any transactions currently in progress within the sessions. It ends the communications connection with the messaging server. It releases the memory and other internal resources used by the connection in the underlying CRTL.

Exception listeners in C++

Using an exception listener is similar in principle to using a message listener. A C++ application must define an exception listener class that is based on the abstract class CExceptionListener. The exception listener class defined in the application must provide an implementation of the onException() method. The application can then instantiate the class to create an exception listener, and register the exception listener with a connection by calling the setExceptionListener() method. Subsequently, if C++RTL detects a problem with the connection, the onException() method is invoked to pass an exception to the application.

To stop the asynchronous reporting of problems with a connection, the application can call the setExceptionListener() method by passing a null pointer as the parameter instead of a pointer to an exception listener. Unless the registration of an exception listener is cancelled in this method, the exception listener must exist for as long as the connection exists.

As the C++RTL passes a pointer to an exception when it calls the onException() method, the application must release the exception by using the C++ delete operator.

The following code fragment provides an example of a exception listener class implementation with an onException() method:


#include <cppHeaders.h>
#include <iostream>
using namespace std;
// Using cpp native rtl namespace
using namespace cppnativertl;
class CMyExceptionListener: public CExceptionListener
{
      public:
               void onException (CFioranoException* pException)
               {
                     try
                     {
                            if(pException->getMessage() != NULL)
                                    cout << "Exception is ::"<<pException->getMessage()<<endl;
                            delete pException;
                     }
                     catch (CJMSException *je)
                     {
                            je->printStackTrace();
                            je->clearException();
                            delete je;
                      }
              }
};
int main(int argc,char* argv[])
{
     CHashTable *m_env;
     CInitialContext *m_ic;
     // Queue Connection Factory
     CQueueConnectionFactory *m_qcf;
     // Queue Connection
     CQueueConnection *m_qc;
     CQueue *m_queue;
     CExceptionListener *m_exp;
     // Set up the environment variables
     m_env = new CHashTable();
     m_env->Put(SECURITY_PRINCIPAL, "anonymous");
     m_env->Put(SECURITY_CREDENTIALS,"anonymous");
     m_env->Put(CONNECT_URL,"http://localhost:1856");
     m_env->Put(BACKUP_URLS,"http://localhost:1956");
      m_env->Put(PROVIDER_URL,"http://localhost:1856");
     try
     {
          m_ic = new CInitialContext(m_env);
          m_qcf = dynamic_cast<CQueueConnectionFactory *>(m_ic->Lookup("primaryQCF"));
          if(m_qcf == 0)
          {
                throw new CJMSException("Connection Factory lookup failed.Error in Type casting.");
          }
          m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");
          m_queue = dynamic_cast<CQueue *>(m_ic->Lookup("primaryQueue"));
          if(m_queue == 0)
          {
               throw new CJMSException("Destination lookup failed.Error in Type casting.");
          }
          m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");
          m_exp = new CMyExceptionListener();
          m_qc->setExceptionListener(m_exp);
          //Other code here
          //Deleting the objects
          delete m_env;
          if(m_qc != NULL)
              m_qc->close();
          delete m_qc;
          delete m_exp;
          delete m_qcf;
          delete m_queue;
          delete m_ic;
     }
     catch(CJMSException *e)
     {
          cout << e->getMessage();
          e->printStackTrace();
          delete e;
     }
     return(0);
}

Advisory Message listeners in C++

Advisory message listener is used to receive asynchronously delivered advisory messages when the reconnection thread is active. Using an advisory message listener is similar in principle to using a message listener.

A C++ application must define an advisory message listener class that is based on the abstract class CAdvisoryMessage. The advisory message listener class defined in the application must provide an implementation of the onAdvisoryMessage() method. The application can then instantiate the class to create an advisory message listener, and register the advisory message listener with a connection by calling the setAdvisoryMessageListener() method. Subsequently, if C++RTL receives asynchronous delivered advisory messages when the reconnection thread is active, the onAdvisoryMessage() method is invoked to pass an advisory message to the application.

To stop the asynchronous reporting problems with a connection, the application can call the setAdvisoryMessageListener() method again, by passing a null pointer as the parameter instead of a pointer to an advisory message listener. Unless the registration of an advisory message listener is cancelled in this method, the advisory message listener must exist as long as the connection exists.

As the C++RTL passes a pointer to an advisory message when it calls the onAdvisoryMessage() method, the application must release the advisory message by using the C++ delete operator.

The following code fragment provides an example of a exception listener class implementation with an onAdvisoryMessage() method:


#include <cppHeaders.h>
#include <iostream>
using namespace std;
// Using cpp native rtl namespace
using namespace cppnativertl;
class CMyAdvisoryMessageListener: public CAdvisoryMsgListener
{
       public:
            void onAdvisoryMessage(CAdvisoryMessage *msg)
            {
                  try

                 {  
                          cout << "Advisory Message is :: " << msg->getAdvisoryMsgString() <<endl;
                          cout << "State of the server is :: " << msg->getAMState() << endl;
                          delete msg;
                }
                catch (CJMSException *je)
                {
                      je->printStackTrace();
                     je->clearException();
                     delete je;
               }
        }
};
int main(int argc,char* argv[])
{
      CHashTable *m_env;
      CInitialContext *m_ic;
      // Queue Connection Factory
      CQueueConnectionFactory *m_qcf;
      // Queue Connection
      CQueueConnection *m_qc;
      CQueue *m_queue;
      CAdvisoryMsgListener *m_adv;
      try
      {
           // Set up the environment variables
           m_env = new CHashTable();
           m_env->Put(SECURITY_PRINCIPAL, "anonymous");
           m_env->Put(SECURITY_CREDENTIALS,"anonymous");
           m_env->Put(CONNECT_URL,"http://localhost:1856");
           m_env->Put(BACKUP_URLS,"http://localhost:1956");
           m_env->Put(PROVIDER_URL,"http://localhost:1856");
           m_ic = new CInitialContext(m_env);
           m_qcf = dynamic_cast<CQueueConnectionFactory *>(m_ic->Lookup("primaryQCF"));
           if(m_qcf == 0)
           {
                throw new CJMSException("Connection Factory lookup failed.Error in Type casting.");
           }
           m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");
           m_queue = dynamic_cast<CQueue *>(m_ic->Lookup("primaryQueue"));
           if(m_queue == 0)
           {
                  throw new CJMSException("Destination lookup failed.Error in Type casting.");
           }
           m_qc = m_qcf->createQueueConnection("anonymous", "anonymous");
           m_adv = new CMyAdvisoryMessageListener();
           m_qc->setAdvisoryMessageListener(adv);

          //Other code here
          //Deleting the objects
          delete m_env;
          if(m_qc != NULL)
                m_qc->close();
          delete m_qc;
          delete m_adv;
          delete m_qcf;
          delete m_queue;
          delete m_ic;
     }
     catch(CJMSException *e)
     {
          cout << e->getMessage();
           e->printStackTrace();
           delete e;
    }
    return(0);
}

Lookup of Administered Objects

FioranoMQ supports lookup of administered objects using the JNDI interface. In case of the CPPRTL, this support has been provided using the CInitialContext class that works mostly on the lines of the InitialContext object of JNDI. Client applications can create an object of CInitialContext and lookup different administered objects from the FioranoMQ Server.

CInitialContext class is the starting context for performing naming operations. The CInitialContext is used to lookup administered objects from the FioranoMQ JNDI store. The client application must use Lookup method of CInitialcontext class to retrieve the named object. The lookup method returns the named object as CLookupHelper which can be dynamically casted at runtime to the required admin object.

The following code fragment provides an example of a CInitialContext class and looking up administered objects.

#include <cppHeaders.h>
#include <iostream>
using namespace std;

// Using cpp native rtl namespace
using namespace cppnativertl;

int main(int argc,char* argv[])
{
      CHashTable *m_env;
      CInitialContext *m_ic;
      // Queue Connection Factory
      CQueueConnectionFactory *m_qcf;
      // Queue Connection
      CQueue *m_queue;

      try
      {
           // Set up the environment variables
           m_env = new CHashTable();
           m_env->Put(SECURITY_PRINCIPAL, "anonymous");
           m_env->Put(SECURITY_CREDENTIALS,"anonymous");
           m_env->Put(CONNECT_URL,"http://localhost:1856");
           m_env->Put(BACKUP_URLS,"http://localhost:1956");
           m_env->Put(PROVIDER_URL,"http://localhost:1856");

           m_ic = new CInitialContext(m_env);

           m_qcf = dynamic_cast<CQueueConnectionFactory *>(m_ic->Lookup("primaryQCF"));
           if(m_qcf == 0)
           {
                throw new CJMSException("Connection Factory lookup failed.Error in Type casting.");
           }

           m_queue = dynamic_cast<CQueue *>(m_ic->Lookup("primaryQueue"));
           if(m_queue == 0)
           {
               throw new CJMSException("Destination lookup failed.Error in Type casting.");
           }

           //Other code here
    }
    catch(CJMSException *e)
    {
          cout << e->getMessage();
          e->printStackTrace();
          delete e;
    }
    return(0);
}

Object Deletion

When an application deletes a cppnativertl object that it has created, cppnativertl releases the internal resources that have been allocated to that object.

When an application creates a cppnativertl object, cppnativertl allocates memory and other internal resources to the object. cppnativertl retains these internal resources until the application explicitly deletes the object by calling the object’s close or delete method. The application should close the MessageConsumer, MessageProducer, QueueBrowser, Requestor, Session, and Connection objects before deleting it.

The application should close/delete MessageConsumer, MessageProducer, QueueBrowser, and Requestor objects before closing/deleting Session object and then connection object should be closed/deleted. MessageListener, ExceptionListener, AdvisoryMessageListener, and administered objects such as Connectionfactory, Destination (Queue, Topic) should be deleted only after closing/deleting the connection object. The cppnativertl releases the internal resources of the associated object that has been deleted.

Also for users convenience deletion of a object internally closes the object and then frees its memory. Deletion of Session object internally closes and deletes all the MessageProducer, MessageConsumer and Browser objects created under that session and then closes the session and frees its memory. Similarly deletion of a connection object internally closes and deletes all the session objects created under that connection and then  closes the connection and frees its memory.

The following objects should be deleted in application side:

  • MessageConsumer, MessageProducer, QueueBrowser and Requestor
  • Session
  • Connection
  • MessageListener, ExceptionListener and AdvisoryMessageListener
  • Administered objects such as Connectionfactory, Destination (Queue, Topic)

Getting Message Properties

The CProperty class wraps the message property value, which includes the value type, size and the value itself.

The CMessage class has a member function getPropertyNames which returns an CEnumeration object.The CEnumeration object contains all the property names present in the received message.The property names can be retrieved from the CEnumeration object using nextElement method.The nextElement method returns void* and it should be type casted to const char*. By using getProperty method of CMessage class which will return CProperty object, all the property values can be retreived.

The following code segments gives an example of using CProperty class.

class CMyMessageListener: public CMessageListener
{
     public:
               void onMessage(CMessage *msg)
               {
                     CEnumeration *propertyNames = NULL;
                     CTextMessage *cmsg = (CTextMessage *)msg;
                     propertyNames = cmsg->getPropertyNames();
                     CProperty *value;
                     while(propertyNames->hasMoreElements())
                     {
                              std::string name;
                              PropertyIndex type;
                              name = (const char*)propertyNames->nextElement();
                              cout << "property name " << name.c_str() << endl;
                              value = cmsg->getProperty(name.c_str());
                              type = value->getPropertyType();
                              switch(type)
                              {
                                   case StringIndex:
                                   {
                                        mqcstring val = cmsg->getStringProperty(name.c_str());
                                        break;
                                   }
                                   case 0:
                                   {
                                         mqbyte b = cmsg->getByteProperty(name.c_str());
                                         break;
                                   }
                                   case ByteArrayIndex:
                                           cout << "ByteArray" << endl;
                           }
                          //Other datatype checks here
                  }
                  cout << "Received Message :: " << cmsg->getText() <<endl;
                  delete msg;
            }
};

Adaptavist ThemeBuilder EngineAtlassian Confluence