Messaging API framework6760911Abstract The Messaging API Framework is an MQ Series gateway allowing distributed C++ applications to communicate with little effort. The system is designed with an object-oriented paradigm in mind, and seamlessly integrates MQ Series' messaging technology. The application developers do not need to obtain any in-depth knowledge of MQ Series, rather, they can use it the same way they do any other C++ objects. More specifically, where MQ Series is to be used to allow communication between two end points, namely, a CORBA-based application in a CORBA environment, and a destination application, a layer of abstraction, or Application Programming Interfaces (APIs), built on top of MQ Series APIs is highly desirable to alleviate the burden of CORBA-based application programmers to handle the complexity of MQ Series programming. This layer could take the form of a framework. The Messaging API Framework of the present invention is intended to be such a layer. The framework is a self-contained module encapsulating the API's needed by the CORBA-based application to use MQ Series. The Messaging API Framework provides a number of APIs, allowing the programmers to place a request, to poll for a request, to send a reply, to receive a reply, and to send a request and block for a reply. Distributed applications can also use this system to communicate with the mainframe applications. In its most preferred embodiment the Messaging API Framework also assures any MQ Series problems are tracked in a self-contained module through circular logging. Claims What is claimed is: Description FILED OF THE INVENTION
##########################################
# Queue managers
# Alert: DO NOT USE REMOTE QUEUE MANAGER
# RMT_Q_MGR =
##########################################
LCL_Q_MGR = DAY2KTO2
##########################################
# Local queues
#
# local request queue LCL_REQUEST_Q
# local reply queue LCL_REPLY_Q
##########################################
LCL_REQUEST_Q = DAY2KTO2.R
LCL_REPLY_Q = DAY2KTO2.X
##########################################
# RMT_REQUEST_Q is the local definition of the remote
# request queue
#
# RMT_REPLY_Q is the local definition of the remote
# reply queue
##########################################
RMT_REQUEST_Q = REMOTE.R
RMT_REPLY_Q = REMOTE.X
##########################################
# Log file name
##########################################
LOG_FILE_NAME = VcsMq.log
##########################################
# Timeout
# When placing MQ calls, specify here how long in
# seconds to wait
##########################################
GET_WAIT_SECS = 10
After reading the configuration file and construction of VcsMq objects, the application needs only allocating message buffers, providing the length of the buffers, and may also declare variables of VCSMQID which is a class to store MQ Series' message ID and correlation ID. Upon completing these steps, invoking VcsMq methods is straightforward, as will be illustrated in the sample programs below. Application Programming Interfaces (APIs) The primary programming interfaces are encapsulated by the class VcsMq. The following elaborates each of the methods. All other interfaces related to Messaging API Framework are group into "others" section. A summary of these exemplary API's is provided as follows. All messaging interfaces may throw VcsMqException exceptions; error messages are also written to a log file with time stamp (The log file name is specified in the configuration file). When the size of the log file grows to reach a certain limit (MAX_LOG_SIZE), all its information is stored in a backup file with the same name appended with ".backup", and the log file itself is truncated to zero length, and starts anew. readConfigFile Signature: void readConfigFile( ) It takes no parameter, and does not return a value. This method reads the Messaging API Framework configuration file. It actually delegates the task to VcsMqConfig objects. This is an alternative way to obtain information in the configuration file. Another approach is to construct the VcsMqConfig first, then read the configuration file (discussed below), and finally construct the VcsMq objects. This method catches and throws VcsMqException related to file and file I/O errors, thrown by objects of VcsMqConfig. pollRequest Signature: long pollRequest(unsigned char* outBuffer, long outLength, VCSMQIDI* id) Where: outBuffer--points to the buffer allocated by the user to receive the request message; outLength is the length of outBuffer; id points to the user allocated space to store VCSMQID of the request message received. The actual length of the request message is returned as the return value. All three parameters are required. This method tries to retrieve a request message from the local request queue, and may throw VcsMqException exceptions. The exception may be caused by MQ Series "No Message Available" condition, which may not be fatal depending on the program logic. A C++ program may catch such an exception, ignore it, and continue to poll for requests. getReply Signature: long getReply(unsigned char* outBuffer, long outLength, VCSMQIDI* id) Where: outBuffer points to the buffer allocated by the user to receive the request message; outLength is the length of outBuffer; id points to a variable of VCSMQID where a reply that matches the id will be received. The actual length of the request message is returned as the return value. All three parameters are required. This method tries to receive a reply message from the local reply queue, and may throw VcsMqException exceptions. The id must be specified by the application before invoking this method. Only the reply message whose message ID and correlation ID match those contained in the id can be successfully received. This id can be obtained by a previous putRequest( ) call, as discussed below. putRequest Signature: VCSMQID putRequest(unsigned char* inbuffer, long inLength) Where: inBuffer points to the request message buffer prepared by the application; inLength is the length of the inBuffer; This method, upon successful sending the request, will return both the message ID and the correlation ID, packaged into VCSMQID. Both parameters are required. This method tries to place a request message into the local definition of the remote request queue, and have the queue managers collaborate to deliver the request message to the remote request queue. This method, after putting the request, also wait on the local reply queue for a COA., which is reported by the remote queue manager when the original request succefully arrives at the remote request queue. The returned VCSMQID may be saved by the caller if it chooses to receive a reply corresponding the original request at a later time. VcsMqException exceptions may be thrown by this method. putReply Signature: void putReply(unsigned char* inBuffer, long inLength, VCSMQID* id) Where: inBuffer points to the reply message buffer prepared by the application; inLength is the length of the inBuffer; id points to a variable of VCSMQID containing the message ID and the correlation ID. This method does not return a value. All parameters are required. This method tries to send a reply message to the remote reply queue in response to a request with the same VCSMQID id. Like putRequest, this method also checks for COA. VcsMqException exceptions may be thrown by this method. putAndGet Signature: long putAndGet(unsigned char* inBuffer, long inLength, unsigned char* outBuffer, long outLength) Where: inBuffer points to a request message buffer prepared by the application; inLength is the length of the inBuffer; outBuffer points to a buffer to receive the reply message; outLength is the length of the outBuffer. This method returns the actual length of the message successfully received. All parameters are required. This method tries to send a request message to the local definition of the remote request queue and wait on the local reply queue for a reply. It combines all the operations of putRequest and getReply methods. It re-throws any exception caught. Version Signature: char* version( ) This method does not take any parameter, and returns the current version of Messaging API Framework as a null-terminated character string. Others The class VcsMqException is used to construct any Messaging API Framework related exceptions. It contains an error code and a text string to explain the error condition. Two methods are available to return the two respective values. The class VcsMqConfig is used to read the configuration file and to store the relevant information. Interfaces are available to retrieve the attributes, and a read method is provided to read the configuration file. This read method may throw file and file I/O related VcsMqException exceptions. Example Programs/Routines Four exemplary programs representing each of the fundamental commands are provided: HelloGet (corresponding to Poll Request), HelloPutMT (corresponding to Send Request), HelloReply (corresponding to Send Reply), and HelloWaitReplyMT (corresponding to Receive Reply). HelloGet is a single threaded program that waits on the local request queue. It prints out the message received. It reports an exception caught; except for "No Message : Available" condition, the program exits. HelloGet Example Code
///////////////////////////////////////////////////////////////////////////
//////////
//
// File HelloPut.cpp
//
// Use only one thread
//
///////////////////////////////////////////////////////////////////////////
//////////
#include <string.h>
#include <iostresm.h>
#include "VcsMq.hpp"
#include "VcsMqException.hpp"
int usage(char* prog) ;
int main(int argc, char** argv)
{
if (srgc != 2) return usage(argv[0]) ;
try
{
VcsMq listener(argv[1]) ;
listener.readConfigFile( ) ;
while (TRUE)
{
VCSMQID id;
long len;
char buffer[256] ;
memset(buffer, `.backslash.0`, sizeof(buffer) ) ;
try
{
len = listener.pollRequest((unsigned char*)
&buffer[0], sizeof(buffer), &id ) ;
}
catch (VcsMqException &e)
{
if (e.code( ) == VCSMQERR_NO_MSG) continue;
else
{
throw;
}
}
if (buffer[0] != `.backslash.0`)
{
buffer[len] = `.backslash.0` ;
cout << "Got:" << buffer << end1 <<
end1;
}
}
}
catch (VcsMqException &e)
{
e.print( ) ;
}
catch( . . . )
{
cout << "Some exception caught" << end1;
}
return 0;
}
int usage(char* prog)
{
cout << "Usage:" << end1;
cout << " " << prog << " <Config file>"
<< end1 << end1;
return 1;
}
HelloPutMT is a multithreaded program that puts a message to the remote request queue, waits on the local reply queue for a COA, and exits. The thread exits when an exception is caught. HelloPutMT Example Code
///////////////////////////////////////////////////////////////////////////
//////////
//
// File HelloPutMT.cpp
//
// Use multiple threads
//
///////////////////////////////////////////////////////////////////////////
//////////
#include <string.h>
#include <iostream.h>
#include <stdio.h>
#include "VcsMq.hpp"
#include "VcsMqException.hpp"
#if defined (NT)
#include <windows.h>
#include <process.h>
#elif defined (AIX)
#include <pthread.h>
#include <unistd.h>
#endif
typedef struct
{
int id;
} ThreadArgs;
VcsMqConfig configData;
const static int NUMTHREADS = 5;
#if defined (NT)
HANDLE hArray [NUMTHREADS] ;
#define THREADTYPE void
#elif defined (AIX)
int trvalue [NUMTHREADS] ;
#define THREADTYPE void *
#endif
int usage(char* prog) ;
THREADTYPE worker(void* args);
int main(int argc, char** argv)
{
int i;
ThreadArgs threadArgs [NUMTHREADS] ;
#if defined (AIX)
int j;
int *val[NUMTHREADS] ;
pthread_t tid[NUMTHREADS] ;
#endif
if (argc != 2) return usage(argv[0] ) ;
for (i=0; i<NUMTHREADS; i++)
{
#if defined (NT)
hArray[i] = CreateEvent (NULL, FALSE,FALSE,NULL) ;
#elif defined (AIX)
val[i] = new int;
#endif
}
configData.fileName(argv[1] ) ;
configData.read( ) ;
for (i=0; i<NUMTHREADS; i++)
{
threadArgs[i].id = i;
#if defined (NT)
unsigned long hTemp = beginthread(worker, 0,
(void*)&(threadArgs[i]) ) ;
if ( (long)hTemp == (long)-1 )
{
cout << "_beginthread failed." << end1;
}
else
{
cout << "_beginthread returned handle " <<
hTemp << end1;
}
#elif defined (AIX)
if (pthread_create(&tid[i] , NULL, worker,
(void*)&(threadArgs[i]) ) )
{
cout << "Thread creation was not successful." <<
end1;
}
else
{
cout << "Thread " << tid[i] << "created."
<< end1;
}
#endif
}
#if defined (NT)
WaitForMultipleObjects(NUMTHREADS, hArray, TRUE,
INFINITE) ;
#elif defined (AIX)
for (j=0; j<NUMTHREADS; j++)
{
int err = pthread_join( tid[j], (void**) &val[j] ) ;
cout << "pthread_join returned " << err <<
end1;
}
for (j=0; j<NUMTHREADS; j++)
{
delete val[j] ;
}
#endif
return 0;
}
THREADTYPE worker(void* args)
{
ThreadArgs *myid = (ThreadArgs*) args;
try
{
// Because all VcsMq calls are thread safe,
// we may as well declare the sender in the
// main program and pass it here through
// ThreadArgs, or, alternatively, declare
// it as global in this sample
//
// Here only shows this approach is OK
// but may not be as efficient as that
// demonstrated in the sample program
// HelloWaitReplyMT
VcsMq sender(configData) ;
VCSMQID VcsMqId;
char buffer[256] ;
sprintf(buffer, "Hello from thread %d", myid->id) ;
for (int i = 0; i < 10; i++ )
{
sender.putRequest( (unsigned char*) &buffer[0],
strlen(buffer) ) ;
}
}
catch (VcsMqException &e)
e.print( ) ;
}
catch( . . . )
{
cout << "Some exception caught" << end1;
}
#if defined (NT)
SetEvent( hArray[myid->id] ) ;
_endthread( ) ;
#elif defined (AIX)
// This value not used in this example, so set to 0
trValue[myid->id] = 0;
pthread_exit (&trValue[myid->i] ) ;
return 0;
#endif
}
int usage(char* prog)
{
cout << "Usage:" << end1;
cout << " " << prog << " <config file>" end1
<< end1;
return 1;
}
HelloReply is a single threaded program that polls requests on the local request queue. Once a request is received, it forms a reply and sends the reply back to the remote reply queue, and then waits on its local reply queue for a COA. The program exits when catching an exception other than "No Message Available". HelloReply Example Code
///////////////////////////////////////////////////////////////////////////
//////////
//
// File HelloReply.cpp
//
// Use only one thread
//
///////////////////////////////////////////////////////////////////////////
//////////
#include <stcing.h>
#include <iostream.h>
#include <ctype.h>
#include "VcsMq.hpp"
#include "VcsMqErr.hpp"
#include "VcsMqException.hpp"
int usage (char* prog) ;
int main(int argc, char** argv)
{
long i;
if (argc != 2) return usage(argv[0] ) ;
try
{
VcsMq listener(argv[1] ) ;
listener.readconfigFile( ) ;
while (TRUE)
{
VCSMQID id;
long len;
char buffer[256] ;
memset(buffer, `.backslash.0`, sizeof(buffer) ) ;
// do not exit when "No Message" is received and timed out
try
{
len = listener.pollRequest((unsigned char*)
&buffer[0] , sizeof(buffer), &id) ;
}
catch (VcsMqException &e)
{
e.print( ) ;
if ( e.code( ) == VCSMQERR_NO_MSG)
{
cout << ".backslash."No Message (" <<
VCSMQERR_NO_MSG
<< ").backslash." is not fatal. "
<< "Continue to poll request . . ." <<
end1 << end1;
continue;
}
else throw;
}
catch( . . . )
{
throw;
}
buffer[len] = `.backslash.0`;
if (buffer[0] != `.backslash.0`)
{
cout << "Got:" << buffer << end1 <<
end1;
}
// put it back
for (i=0; i<len; i++)
{
if (islower(buffer[i]))
buffer[i] = toupper(buffer[i] ) ;
else if (isupper(buffer[i] ) )
buffer[i] = tolower(buffer[i]) ;
}
listener.putReply( (unsigned char*) &buffer[0] ,
strlen(buffer) , &id) ;
}
}
catch(VcsMqException &e)
{
e.print( )
}
catch( . . . )
{
cout << "Some exception caught" << end1;
}
return 0;
{
int usage(char* prog)
{
cout << "Usage:" << end1;
cout << " " << prog << " <Config file>"
<< end1 << end1 ;
return 1;
}
HelloWaitReply is a multithreaded program that sends a request message to the remote request queue, waits on its local reply queue for a COA, and a reply. Threads exit when catching exceptions. HelloWaitReplyMT Example Code
///////////////////////////////////////////////////////////////////////////
//////////
//
// File HelloWaitReplyMT.cpp
//
// Use multiple threads
//
///////////////////////////////////////////////////////////////////////////
//////////
#include <string.h>
#include <iostream.h>
#include <stdio.h>
#include "VcsMq.hpp"
#include "VcsMqException.hpp"
#if defined (NT)
#include <windows.h>
#include <process.h>
#elif defined (AIX)
#include <pthread.h>
#include <unistd.h>
#endif
typedef struct
{
int id;
VcsMq* obj;
} ThreadArgs;
VcsMqConfig configData;
const static int NUMTHREADS = 5;
#if defined (NT)
HANDLE hArray [NUMTHREADS] ;
#define THREADTYPE void
#elif defined (AIX)
int trValue [NUMTHREADS] ;
#define THREADTYPE void *
#endif
int usage(char* prog) ;
THREADTYPE worker(void* args) ;
// Note:
// Outputs to screen are not protected by mutex
// so outputs from different threads may interfere
// with each other. That is find for this sample
int main(int argc, char** argv)
{
int i;
ThreadArgs threadArgs [NUMTHREADS] ;
#if defined (AIX)
int j;
int *val [NUMTHREADS] ;
pthread_t tid[NUMTHREADS] ;
#endif
if (argc != 2) return usage(argv[0] ) ;
for (i=0; i<NUMTMREADS; i++)
{
#if defined (NT)
hArray[i] = CreateEvent (NULL, FALSE, FALSE, NULL) ;
#elif defined (AIX)
val[i] = new int;
#endif
}
configData.fileName(argv[1] ) ;
configData.read( ) ;
// Because all methods of VcsMq are thread-safe,
// we are using the same VcsMq object for all
// threads. This approach is different from
// the sample program HelloPutMT
VcsMq sender(configData) ;
for (i=0; i<NUMTHREADS; i++)
{
threadArgs[i].id = i;
threadArgs[i] .obj = &sender;
#if defined (NT)
unsigned long hTemp = _beginthread(worker, 0,
(void*)&(threadArgs[i] ) ) ;
if ( (long)hTemp == (long)-1 )
{
cout << "_beginthread failed." << end1;
}
else
{
cout << "_beginthread returned handle " << hTemp
<<
end1;
}
#elif defined (AIX)
if (pthread_create(&tid[i], NULL, worker,
(void*)&(threadArgs[i] ) ) )
{
cout << "Thread creation was not successful." <<
end1;
}
else
{
cout << "Thread " << tid[i] << " created."
<< end1;
}
#endif
}
#if defined (NT)
WaitForMultipleObjects(NUMTHREADS, hArray, TRUE,
INFINITE) ;
#elif defined (AIX)
for (j=0; j<NUMTHREADS; j++)
{
int err = pthread_join( tid[j], (void**) &val[j] ) ;
cout << "pthread_join returned " << err << end1;
}
for (j=0; j<NUMTHREADS; j++)
{
delete val[j] ;
}
#endif
return 0;
}
THREADTYPE worker(void* args)
{
ThreadArgs *myid = (ThreadArgs*) args;
VcsMq* sender = myid->obj;
long len;
try
{
char buffer[256] ;
char buffer2[256] ;
sprintf(buffer, "Hello from thread %d", myid->id) ;
for (int i = 0; i < 10; i++ )
{
len = sender->putAndGet( (unsigned char*) &buffer[0],
strlen(buffer), (unsigned char*) &buffer2[0],
sizeof(buffer2) ) ;
#ifdef _DEBUG
buffer2[len] = `.backslash.0` ;
cout << "Got Reply: " << buffer2 << end1;
#endif
}
}
catch(VcsMqException &e)
{
e.print( ) ;
}
catch( . . . )
{
cout << "Some exception caught" << end1;
}
#if defined (NT)
SetEvent ( hArray[myid->id] ) ;
_endthread( ) ;
#elif defined (AIX)
// This value not used in this example, so set to 0
trValue[myid->id] = 0;
pthread_exit (&trValue[myid->id] ) ;
return 0;
#endif
}
int usage(char* prog)
{
cout << "Usage:" << end1;
cout << " " << prog << " <Config file>"
<< end1 << end1;
return 1;
}
Multiple processes of these programs can be run simultaneously. To run the programs, make sure the MQ channels are up and running, then enter the program name followed by the configuration file name. HelloGet and HelloPutMT work together; HelloReply and HelloWaitReplyMT work together. Example: If the HelloGet is to use get cfg as the configuration file and HelloPutMT is to use put cfg as the configuration file, then in one console, go to the directory containing the program and issue: ./HelloGet get cfg In another console, go to the same directory as above and type: ./HelloPutMT put cfg In summary, the Messaging API Framework is an MQ Series gateway allowing distributed C++ applications to communicate with little effort. The system is designed with an object-oriented paradigm in mind, and seamlessly integrates MQ Series' messaging technology. The framework is a self-contained module encapsulating well-defined interfaces and exceptions. The framework maintains flexibility by providing simple and flexible MQ Series initialization and by maintaining circular logging for easy problem determination in its most preferred embodiment. The application developers do not need to obtain any in-depth knowledge of MQ Series, rather, they can use it the same way they do any other C++ objects. Although only a few embodiments of the present invention have been described, it should be understood that the present invention may be embodied in many other specific forms without departing from the spirit or the scope of the present invention. Therefore, the present examples are to be considered as illustrative and not restrictive, and the invention is not to be limited to the details given herein, but may be modified within the scope of the appended claims along with their full scope of equivalents.
|
Same subclass Same class Consider this |
||||||||||
