The WebSphere MQ Everyplace C API is object based, using handles to the objects as parameters. When an API function is called, you should always pass or receive a handle to an object. (There are a few exceptions but these are carefully noted in the WebSphere MQ Everyplace C Programming Reference.) The function called then acts on the object that is represented by the handle. The life cycle of the handles (and consequently the objects they represent) needs to be carefully managed. Functions are provided to create and free handles. Failure to free handles is equivalent to failure to free resources.
Handles are references to objects, and it is possible for handles to reference NULL objects. These handles should still be freed, as the handle itself uses resources (although these are minimal). A macro IS_NULL(pObjectHandle) is provided to determine if a handle points to a null object, as passing a handle to a null object into a API function can put the system into an unstable state.
Most functions take a handle and an exception block. The handle indicate the object that the function is operating on. The exception block is specific to a thread and provides the ability to communicate errors back to the caller. Some functions do not take a handle. These are Static functions that do not operate on an object. Examples are the Session and terminate and initialization functions (discussed in Session). Functions that create new object handles do not take a handle as an input parameter, instead they return a new handle
All functions return MQERETURN values to indicate success or failure. Data is returned using output parameters, typically indicated by pointers to object handles or by pointers to primitive types.
Prototypes follow two basic forms:
MQERETURN mqeObjectName_new(MQeExceptBlock *pErrStruct, MQeObjectHndl *phNewObjectHndl, .... <parameter>);
MQERETURN mqeObjectName_functionName(MQeObjectHndl hObjectHndl, MQeExceptBlock *pErrStruct, ... <parameters>);
The handle of the object on which the function operates always comes first. With new functions, the exception block comes first as at that point there is no object to operate on.
For each new call there is a matching free call.
MQERETURN mqeObjectName_free (MQeObjectHndl hObjectHndl, MQeExceptBlock * pErrStruct);
To create a new object, and get the handle to that object, call the mqeObjectName_new(...) function. To release the resource associated with a handle, call the mqeObjectName_free(...) function.
When creating and freeing handles, the following rules should be observed:
Note that for Administration messages, each message has its own unique new() function, but there is only one free function, mqeAdminMsg_free.
The underlying design of the C API is object based, and a hierarchy of objects can be established. The hierarchy reflects specialization of object types, an example is the Message object which is a specialization of the Fields object (seeFundamental objects).
As an example, assume that we have an MQeVehicle object and an MQeCar object. MQeCar is a specialization of MQeVehicle. Both of these objects could have the function move() defined on them, but MQeCar also has a fillPetrol() function. Both objects must also have new() and free() functions to create new objects and to return the handles.
Assume the following (using standard WebSphere MQ Everyplace API conventions):
mqeVehicle_new(MQeExceptBlock *pErrStruct, MQeVehicleHndl *phNewVehicleHndl); mqeVehicle_free(MQeVehicleHndl hVehicleHndl, MQeExceptBlock *pErrStruct); mqeVehicle_move(MQeVehichleHndl hVehicleHndl, MQeExceptBlock *pErrStruct); mqeCar_new(MQeExceptBlock *pErrStruct, MQeCarHndl *phNewCarHndl); mqeCar_free(MQeCarHndl hCarHndl, MQeExceptBlock *pErrStruct); mqeCar_fillPetrol(MQeCarHndl hCarHndl, MQeExceptBlock *pErrStruct);
New objects can be created like this:
MQeVehicleHndl hMyVehicle; MQeCarHndl hMyCar; rc = mqeVehicle_new(&exceptBlock, &hMyVehicle); rc = mqeCar_new(&exceptBlock, &MyCar);
Using the handle to the MQeCar object you can fill the car with petrol:
rc = mqeCar_fillPetrol(hMyCar, &exceptBlock);
Note that the following call would fail, with a Return Code 10400 - MQERETURN_INVLAID_ARGUMENT, because MQeVehicle is not specialization of MQeCar.
rc = mqeCar_fillPetrol(hMyVehicle, &Block)
Both of the following calls are valid, because MQeCar is a specialization of MQeVehicle:
rc = mqeVehicle_move(hMyCar, &exceptBlock); rc = mqeVehicle_move(hMyVehicle, &exceptBlock);
Such functions are known as polymorphic. Potentially different implements are called depending on the nature of handle you pass in.
To free the storage you should call the following functions
rc = mqeCar_free(hMyCar, &exceptBlock); rc = mqeVehcile_free(hMyVehicle, &exceptBlock);
As discussed in the previous section, if object A is a specialization of object B, object A's handle can be used in functions defined on object B. When the application is built, the checks are made to see if the use of the handles is valid. Three levels of static type checking can be used:
The middle warning level is the default, but this can be changed as
follows:
Definition | Type checking level |
---|---|
#define MQEHANDLE_WARNING_LEVEL 1 | No warnings at all |
#define MQEHANDLE_WARNING_LEVEL 2 | Default level |
#define MQEHANDLE_WARNING_LEVEL 3 | Maximum warnings |
Using the example above, on the maximum warning level, the call
rc = mqeVehicle_move(hMyCar, &exceptBlock);
generates a compiler warning.
To remove this warning, convert the call to:
rc = mqeVehicle_move((MQeVehicleHndl)hMyCar, &exceptBlock);
On the lower two warning levels, this would not generate a warning.
The only overhead of changing the warning level is in the number of warnings produced during compilation.