Retrieve message queue contents in PHP

[adrotate group=”3,4,7″]
We have been looking at how to pull a list of messages from a message queue and show them via PHP to a browser for our Dr4i product.

We have asked EasyCom if they would add support for message queues to their native i5_toolkit but we have not heard back yet. That’s not to say EasyCom are slow in coming back, infact we are very happy with the help and support they have given us on this problem alone. They helped us tremendously in how to build the PHP array containing an array of structures which was passed to the service program on the IBM i. Its a real pity Zend does not provide the same level of support as EasyCom has, we have been trying to help others who post requests into the Zend forums which is where most people end up when they are having problems but Zend seem to pay little attention to them? Yes you can use the Zend Support you get for free for the first year but after that you are on your own unless you buy into one of their annual support contracts which are not cheap.

Anyhow enough of the bitching, we like EasyCom and thats all there is to it!

We have provided enough for you to take this to the next step but be aware the following code is incomplete. It does show you how to use a structure in PHP, something we have seen many requests for help in the Zend forums about which was the reason we felt we would post it. We have also posted the code we used in our service program to show how it receives the request. Thanks to EasyCom for giving us the heads up on how to improve the parameter passing in PHP! (See how much we like EasyCom!)

Here is the PHP Code, its a function that is called from the main page. We have defined $maxmsg in the config file and set it to 200, it can be set to what ever you like, the rest is pretty straight forward now we hav worked out how to build the parameters for the call.

function Get_MsgQ_List(&$conn,$br) {
include 'config.php';
$desc = array(
	array("Name" => 'NumEnt', "io" => I5_INOUT, "type" => I5_TYPE_INT),
	array("DSName" =>"msg", "CountRef" => 'NumEnt', "DSParm" => array(
		array("Name" => "msgid", "io" => I5_OUT, "type" => I5_TYPE_CHAR, "length" => "7"),
		array("Name" => "msgkey", "io" => I5_OUT, "type" => I5_TYPE_CHAR, "length" => "8"),
		array("Name" => "dta", "io" => I5_OUT, "type" => I5_TYPE_CHAR, "length" => "132"))));

$prog = i5_program_prepare("SVRSTS(Get_MsgQ_List)", $desc );
if ($prog === FALSE) {
	$errorTab = i5_error ();
	echo "Program prepare failed 
n"; var_dump ( $errorTab ); die (); } $parameter = array("NumEnt" => $maxmsg); $parmOut = array("NumEnt" => "nbr", "msg" => "messages"); $ret = i5_program_call($prog, $parameter, $parmOut); if (!$ret) { throw_error("i5_program_call"); exit(); } ?> <div class="infocontent"> <table><?php for($i = 0; $i < $nbr; $i++) { echo('<tr><td>' .$messages[$i][msgid] .'</td><td>' .$messages[$i][dta] .'</td></tr>'); } ?> </table> </div><?php return; }

Here is the C code, we are building the list synchronously so it will put all of the messages into the receiver at once, we are pulling the data back using the QGYGTLE API. We have told the API we only want 200 messages as this was passed in via the num variable.

typedef _Packed struct  Msg_Ret_x {
                        int Num;
                        char Q_Inf[2][20];
                        }Msg_Ret_t;

typedef _Packed struct  SelInf_x {
                        char  List_Direction[10];
                        char  Reserved[2];
                        int   Severity_Criteria;
                        int   Max_Msg_Length;
                        int   Max_Help_Length;
                        int   Sel_Criteria_Offset;
                        int   Num_Sel_Criteria;
                        int   Start_Msg_Keys_Offset;
                        int   Retd_Fields_IDs_Offset;
                        int   Num_Fields;
                        char  Sel_Cri[1][10];
                        char  Msg_Key[1][4];
                        int   FieldID[2];
                        }SelInf_t;

typedef _Packed struct  Msg_Dets_x {
                        char MsgID[7];
                        char MsgKey[8];
                        char MsgDta[132];
                        } Msg_Dets_t;

int Get_MsgQ_List(int *num, Msg_Dets_t data[]) {
int Ret_Recs = 0;                         /* records to return */
int i;                                      /* counter */
int Num_Recs = 0;
char Sort_Info = '0';                       /* msg sort */
char ListInfo[80];                          /* list info */
char QueueInfo[21] = "1DR4IMSGQ  *LIBL     "; /* msgq to list */
char Queue_List[44];                        /* holder */
char SPC_Name[20] = "QGYOLMSG  QTEMP     "; /* usrspc for data */
char Msg_Buf[1024];                         /* returned data */
char *space;                                /* pointers */
char *tmp;
char *Data;
char *dta_ptr;
char *m;
char msg_dta[255];
Qgy_Olmsg_ListInfo_t *ret_info;             /* returned hdr */
Qgy_Olmsg_RecVar_t *ret_msg;                /* returned message */
Qgy_Olmsg_IDFieldInfo_t *field_data;        /* returned msg dta */
SelInf_t Sel_Info;                          /* selection info */
Msg_Ret_t *Q_Info;                          /* queue info */
Msg_Dets_t *dets;
EC_t Error_Code = {0};                      /* Error Code */

Error_Code.EC.Bytes_Provided = _ERR_REC;
Ret_Recs = *num;
dets = (Msg_Dets_t *)data;

/* set up the request */
Q_Info = (Msg_Ret_t *)Queue_List;
memcpy(Sel_Info.List_Direction,"*PRV      ",10);
Sel_Info.Severity_Criteria = 0;
Sel_Info.Max_Msg_Length = 132;
Sel_Info.Max_Help_Length = 0;
Sel_Info.Sel_Criteria_Offset = 44;
Sel_Info.Num_Sel_Criteria = 1;
Sel_Info.Start_Msg_Keys_Offset = 54;
Sel_Info.Retd_Fields_IDs_Offset = 58;
Sel_Info.Num_Fields = 2;
memcpy(Sel_Info.Sel_Cri[0],"*ALL      ",10);
memset(Sel_Info.Msg_Key[0],0xFF,4);
Sel_Info.FieldID[0] = 302;
Sel_Info.FieldID[1] = 1001;
/* crt usrspc to hold data */
if(Crt_Usr_Spc(SPC_Name,_16MB) != 1) {
   snd_msg("CRT0002",SPC_Name,20);
   return -1;
   }
QUSPTRUS(SPC_Name,
         &space,
         &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   Error_Code.EC.Bytes_Available = 0;
   QUSDLTUS(SPC_Name,
            &Error_Code);
   if(Error_Code.EC.Bytes_Available > 0) {
      snd_error_msg(Error_Code);
      }
   return -1;
   }
/* list the messages */
QGYOLMSG(space,
         _16MB,
         ListInfo,
         Ret_Recs,
         &Sort_Info,
         &Sel_Info,
         sizeof(Sel_Info),
         QueueInfo,
         Queue_List,
         &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   Error_Code.EC.Bytes_Available = 0;
   QUSDLTUS(SPC_Name,
            &Error_Code);
   if(Error_Code.EC.Bytes_Available > 0) {
      snd_error_msg(Error_Code);
      }
   return -1;
   }
ret_info = (Qgy_Olmsg_ListInfo_t *)ListInfo;
*num = ret_info->Records_Retd;
Num_Recs = ret_info->Records_Retd;
/* Use QGYGTLE to return the messages to the program */
ret_msg = (Qgy_Olmsg_RecVar_t *)Msg_Buf;
/* dta_ptr = dets; */
for(i = 1; i <= Num_Recs; i++) {
   QGYGTLE(Msg_Buf,
           sizeof(Msg_Buf),
           ret_info->Request_Handle,
           &ListInfo,
           1,
           i,
           &Error_Code);
   if(Error_Code.EC.Bytes_Available > 0) {
      snd_error_msg(Error_Code);
      Error_Code.EC.Bytes_Available = 0;
      QUSDLTUS(SPC_Name,
               &Error_Code);
      if(Error_Code.EC.Bytes_Available > 0) {
         snd_error_msg(Error_Code);
         }
      memcpy(dta_ptr,"*ERROR;",7);
      return -1;
      }
   tmp = Msg_Buf;
   tmp += ret_msg->Offset_to_Fields_Retd;
   field_data = (Qgy_Olmsg_IDFieldInfo_t *)tmp;
   Data = tmp;
   Data += sizeof(_Packed struct Qgy_Olmsg_IDFieldInfo);
   memcpy(dets->MsgID,ret_msg->Msg_ID,7);
   Cvt_Hex_Buf(ret_msg->Msg_Key,dets->MsgKey,4);
   memcpy(dets->MsgDta,Data,field_data->Data_Length);
   dets++;
   }
/* clean up the resources  */
QGYCLST(ret_info->Request_Handle,
        &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   Error_Code.EC.Bytes_Available = 0;
   QUSDLTUS(SPC_Name,
            &Error_Code);
   if(Error_Code.EC.Bytes_Available > 0) {
      snd_error_msg(Error_Code);
      }
   return -1;
   }
QUSDLTUS(SPC_Name,
         &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   }
return 1;
}

This results in a page similar to the following, we have not displayed the entire page but it does return all 200 messages with the last message in the queue at the top of the screen. The message queue has over 350 messages in it, we felt displaying the last xnumber was a better solution rather than build an array based on the total number of messages in the queue.

Message list
List of messages from message queue

Please feel free to change the code as you think fit, if you do posting back the changes may help others. We hope it helps explain how to pass in an array of structures to the service program. One issue which is still hanging out is how to pass back the content of the message key which is a hex value stored in a 4 character array! Every attempt to convert this to a true hex value so far results in an empty string! I am sure we will get to the bottom of it but in the meantime if you have something, please post your code…

The screen shot is from our new DR4i product, if you are interested in looking closer at what it can do let us know and we will be happy to give you the full tour..

Comments gratefully accepted, its lonely speaking with yourself!

Chris…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.