fhg
Volume 9, Number 38 -- October 21, 2009

Getting the Message, Part 2

Published: October 21, 2009

by Paul Tuohy

In Part 1 of this series, we saw the definition of a number of message routines (clearMessages(), addMessage(), messageCount(), and getMessage()). In this article, we will see how the routines may be used in both an RPG and a PHP environment. We will also look at another couple of message routines that may be useful.

Remember, a library containing the code used in these articles may be downloaded here.

A Test Procedure

Figure 1 shows a small subprocedure (fillMessages()) used to demonstrate that messages may be added/stored at any level in the job. The subprocedure simply adds a filler message the number of times requested on the passed parameter.

p fillMessages    B                   Export
D                 PI
D  timesToSend                  10i 0 Const

D i               s             10i 0
 /free
  for i = 1 to timesToSend;
     addMessage(APP_FILLER : 'FILL' : %char(i));
  endFor;
 /end-Free
p                 E

Figure 1: Test procedure fillMessages().


In RPG

Figure 2 shows an RPG program used to demonstrate the message routines. The program performs the following:

  • Calls clearMessages() to clear any stored messages.
  • Calls addMessage() twice to add two messages (note the use of the named constants to identify the required message IDs).
  • Calls fillMessages() to add three filler messages.
  • Based on the value returned by messageCount(), the program loops through calls to getMessage() and display the message text returned for each message.
 /Copy QCpySrc,StdHSpec

     // To create the program...
     //    Current library set to MESSAGES
     //    CRTBNDRPG PGM(SHOWMSGS)

 /Copy QCpySrc,BaseInfo

D message         DS                  LikeDS(Def_MsgFormat)

D i               s             10i 0
 /free
  clearMessages();
  addMessage(ERR_NOTFOUND : 'TEST1');
  addMessage(ERR_CHANGED : 'TEST2');

  fillMessages(3);

  for i = 1 to messageCount();
     getMessage(i : message);
     dsply %subst(message.msgText :1 :40);
  endFor;
  *InLR = *on;
 /end-Free

Figure 2: Program SHOWMSGS demonstrates the use of message procedures in RPG.


The expected results from SHOWMSGS are shown in Figure 3:

DSPLY  An expected record was not found for upd
DSPLY  Record already altered. Update/Delete ig
DSPLY  This is filler message 1
DSPLY  This is filler message 2
DSPLY  This is filler message 3

Figure 3: Result of calling SHOWMSGS.


Of course, this program simply shows the format of the calls. The real key is what the program wants to do with the messages it retrieves.

If this were a green-screen program, the returned messages could be sent to a message subfile. If it were a CGIDEV2 program, the returned messages might be used to populate message information on a Web page.

For example, when I am using CGIDEV2, I identify errors on a Web page using two divisions/variables. ERRTEXT contains the message text to be display and ERRVARS (a non-display division on the page) contains the names of the fields in error. A Javascript routine that runs on page load uses the contents of ERRVARS to highlight the fields in error. Figure 4 shows the setCGIMessages() routine used to set the errors on a Web page.

P setCGIMessages  B                   Export
D                 PI

D i               s             10i 0
D msgFormat       DS                  LikeDs(Def_MsgFormat)
D errVars         s          32767a   Varying
D errText         s          32767a   Varying

 /free
  errVars = ' ';
  errText = ' ';

  if messageCount() > 0;
     for i = 1 to messageCount();
        getMessage(i:msgFormat);
        if (i > 1);
           errVars = errVars + '%%';
           errText = errText + '<br />';
        endIf;
        errVars = errVars + %Trim(msgFormat.ForField);
        errText = errText + %Trim(msgFormat.MsgText);
     endFor;
  endIf;
  updHTMLvar( 'ErrVars': errVars);
  updHTMLvar( 'ErrText': errText);
 /end-Free
P                 E

Figure 4: Setting errors with CGIDEV2.


In PHP

Let me start by saying that I am still finding my way with PHP, so those of you who know better, please don't be too harsh on my attempts.

Since I am running this on my i, of course I am using ZendCore to do all the hard work for me.

Whenever one decides to interface between two technologies, there is always a little bit of work to be done, and PHP is no exception. The key point to remember is that no changes are made to the underlying subprocedures.

The first issue with the PHP routines is that they have issues calling subprocedures that don't accept parameters. For that reason, I wrote a wrapper routine (php_clearMessages()), shown in Figure 5, that accepts a dummy parameter and issues a call to the original clearMessages() subprocedure.

P php_clearMessages...
P                 B                   Export
D                 PI
D  dummy                         1a
 /free
  clearMessages();
 /end-Free
P                 E

Figure 5: Wrapper subprocedure for call to clearMessages() from PHP.


The next step was to write PHP functions that would issue calls to the corresponding subprocedures in the UTILITY service program. Figure 6 shows the source of func_messages.php. Each of these functions is simply a PHP function that issues a call to the corresponding subprocedure in the UTILITY service program. In other words, these are PHP wrappers to call the RPG subprocedures.

<?php

define('APP_ERR_NOTFOUND','ALL9001');
define('APP_ERR_CHANGED','ALL9002');
define('APP_ERR_DUPLICATE','ALL9003');
define('APP_ERR_CONSTRAINT','ALL9004');
define('APP_ERR_TRIGGER','ALL9005');
define('APP_ERR_UNKNOWN','ALL9006');
define('APP_ERR_NOT_NUMBER','ALL9007');
define('APP_ERR_NOT_DATE','ALL9008');

function clearMessages($conn) {
	
	$desc = array (array ("name" => "dummy", "io" => I5_INOUT,
                     "type" => I5_TYPE_CHAR, "length" => "1" ) );
	
   $prog1 = i5_program_prepare ( "MESSAGES/UTILITY(php_clearMessages)",
                                $desc, $conn );
	if (! $prog1) {
		die ( "Prepare for clearMessages() did not work" );
	}
	
	$parameter = array ("dummy" => " " );
	$parmOut = array ("dummy" => "dummy" );
	if (! i5_program_call ( $prog1, $parameter, $parmOut )) {
		die ( "Call to clearMessages() did not work" );
	}
	i5_program_close ( $prog1 );

}

function addMessage($conn, $msgId, $forField = " ", $msgData = " ") {
	
	$desc = array (array ("name" => "msgId", "io" => 
	I5_INOUT, 
                            "type" => I5_TYPE_CHAR, "length" => "7" ),
			   array ("name" => "forFieldIn", "io" 
			   => I5_INOUT,
                            "type" => I5_TYPE_CHAR, "length" => "25" ),
			   array ("name" => "msgData", "io" 
			   => I5_INOUT,
                          "type" => I5_TYPE_CHAR, "length" => "500" ) );
	
	$prog1 = i5_program_prepare ( "MESSAGES/UTILITY
	(addMessage)",
                                    $desc, $conn );
	if (! $prog1) {
		die ("Prepare for addMessage() did not work");
	}
	
	$parameter = array ("msgId" => $msgId,
				  "forFieldIn" => $forField,
				  "msgData" => $msgData );
	$parmOut = array ("msgId" => "msgId",
				"forFieldIn" => "forField",
				"msgData" => "msgData" );
	
	if (! i5_program_call ( $prog1, $parameter, $parmOut ) ) {
		die ("Call to addMessage() did not work");
	}
	i5_program_close ( $prog1 );
	
}


function messageCount($conn) {
	
	$desc = array (
		array ("name"=>"numMessages", "io"=>I5_INOUT,
                   "type"=>I5_TYPE_LONG,  "length"=>"4"),
		);
	
	$prog1 = i5_program_prepare("MESSAGES/UTILITY(php_messageCount)",
                                  $desc, $conn);
	if (!$prog1) {
		die("Prepare for messageCount() did not work");
	}
	
	$parameter = array("numMessages"=>0);
	$parmOut = array("numMessages"=>"msgCount");
	
	if (! i5_program_call($prog1, $parameter, $parmOut)){
		die("Call to messageCount() did not work");
	}
	i5_program_close ( $prog1 );
	
	return $msgCount;
}


function getMessage($conn, $forMessage, $forField=" ", $msgId=" ",
                    $help=" ", $severity=0) {
	 
	$desc = array (
		array ("name"=>"forMessage", "io"=>I5_INOUT,
                  "type"=>I5_TYPE_LONG,  "length"=>"4"),
		array("DSName"=>"msgFormat", "DSParm"=>array(
	        	array("Name"=>"msgId", "IO"=>I5_INOUT,
                        "Type"=>I5_TYPE_CHAR, "Length"=>"7"),
	        	array("Name"=>"msgText", "IO"=>I5_INOUT,
                        "Type"=>I5_TYPE_CHAR, "Length"=>"80"),
	        	array("Name"=>"severity", "IO"=>I5_INOUT,
                        "Type"=>I5_TYPE_LONG, "Length"=>"4"),
	        	array("Name"=>"help", "IO"=>I5_INOUT,
                        "Type"=>I5_TYPE_CHAR, "Length"=>"500"),
	        	array("Name"=>"forField", "IO"=>I5_INOUT,
                        "Type"=>I5_TYPE_CHAR, "Length"=>"25"))
	        )
		);
	
	$prog1 = i5_program_prepare("MESSAGES/UTILITY(getMessage)",
                                  $desc, $conn);
	if (!$prog1){
		die("Prepare for getMessage() did not work");
	}
	
	$parameter = array("forMessage"=>$forMessage, 
				 "msgFormat"=>array("msgId"=>" "));
	$parmOut = array("forMessage"=>"forMessage",
			     "msgFormat"=>"msgFormat");
	
	if (! i5_program_call($prog1, $parameter, $parmOut)){
		die("Call to getMessage() did not work");
	}
	
	i5_program_close($prog1);
	$forField = $msgFormat["forField"];
	$msgId = $msgFormat["msgId"];
	$help = $msgFormat["help"];
	$severity = $msgFormat["severity"]; 
	return $msgFormat["msgText"];  
}


function fillMessages($conn, $numMessages) {
	
	$desc = array (
		array ("name"=>"numMessages", "io"=>I5_INOUT,
                   "type"=>I5_TYPE_LONG,  "length"=>"4"),
		);
	
	$prog1 = i5_program_prepare("MESSAGES/UTILITY(fillMessages)",
                                  $desc, $conn);
	if (!$prog1) {
		die("Prepare for fillMessages() did not work");
	}
	
	$parameter = array("numMessages"=>$numMessages);
	$parmOut = array("numMessages"=>"msgCount");
	
	if (! i5_program_call($prog1, $parameter, $parmOut)){
		die("Call to fillMessages() did not work");
	}
	i5_program_close ( $prog1 );
	
	return;
}


?>

Figure 6: PHP functions, func_messages.php.


Note that each of the functions is passed a parameter ($conn), which identifies the connection to be used on the call to the subprocedure. This parameter identifies the job that actually calls the subprocedures. Also note the definition of constants for the message IDs.

Figure 7 shows the PHP script phpmessage.php that corresponds to the SHOWMSGS program. The script performs the following:

  • Makes a connection to the i ($conn) .
  • Calls clearMessages() to clear any stored messages.
  • Calls addMessage() twice to add two messages (note the use of the named constants to identify the required message IDs).
  • Calls fillMessages() to add three filler messages.
  • Based on the value returned by messageCount(), the script loops through calls to getMessage() and display the message text returned for each message.
  • Closes the connection to the i.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<title>Playing with PHP Program Calls</title>
</head>

<body>

<h1>Playing with PHP Messages</h1>

<p>This example demonstrates use of the message routines</p>

<?php 
ini_set('display_errors', 1);
error_reporting(E_ALL & ~E_NOTICE);

require 'func_messages.php';

($conn = i5_pconnect("localhost", "", "",array("I5_OPTIONS_JOBNAME"
=>"PAULPHP")))
or trigger_error("Could not make connection", E_USER_ERROR);

echo "Clear Messages <br />";
clearMessages($conn);

echo "Add Messages <br />";
addMessage($conn, APP_ERR_NOTFOUND, 'TEST1');
addMessage($conn, APP_ERR_CHANGED, 'TEST2');

echo "Fill 3 Messages <br />";
fillMessages($conn, 3);

echo "Get Message count <br />";
$msgCount = messageCount($conn);
echo "Returned message count is ".$msgCount." <br />";

echo "Get Messages <br />";

for ($i = 1; $i <= $msgCount; $i++) {
	echo getMessage($conn, $i)." <br />";
}

i5_close($conn);
?>

<p> Page complete </p>

</body>
</html>

Figure 7: Script phpmessage.php, use of message procedures in PHP.


The expected results from phpmessage.php are shown in Figure 8:



Figure 8: Result of calling phpmessage.php.


Other Message Subprocedures

But we don't have to stop with just these subprocedures. Figure 9 lists some other message subprocedures that you might find useful:

  • addMessageText() allows you to directly store message text as opposed to using a message ID/message file.
  • setMessageFile() allows you to change the default message file and/or library being used.
  • sendFileError() sends file messages based on a status code passed as a parameter. This subprocedure would be called based on an I/O error being received on a file operation and being trapped with an error extender.
P addMessageText  B                   Export
D                 PI
D  msgText                      80a   Const
D  forFieldIn                   25a   Const
D                                     Options(*Omit:*NoPass)
D  severity                     10i 0 Const
D                                     Options(*NoPass)

D forField        S                   like(forFieldIn)
 /free
  msgCount += 1;
  messages(msgCount).msgText = msgText;
  if %Parms() > 2;
    messages(msgCount).severity = severity;
  endIf;
  if %Parms()> 1;
    if %Addr(forFieldIn) <> *Null;
      forField = forFieldIn;
    endIf;
  endIf;
  messages(msgCount).forField = forField;
 /end-Free
P                 E


P setMessageFile...
P                 B                   Export
D                 PI
D  newMsgf                      10A   Const
D  newMsgLib                    10A   Const
D                                     Options(*NoPass)
 /free

  msgFile = newMsgF;
  if %Parms()> 1;
    msgFileLib = newMsgLib;
  endIf;
  return;

 /end-Free
P                 E

P sendFileError   B                   Export

D                 PI              n
D  status                        5i 0 Const

 /free

  select;

      // Duplicate
  when status = STAT_DUPLICATE;
      addMessage(ERR_DUPLICATE);

      // Referential Constraint

  when status = STAT_CONSTRAINT_1  or
      status = STAT_CONSTRAINT_2;
      sendConstraintMsg();

      // Trigger
  when status = STAT_TRIGGER_1  or
      status = STAT_TRIGGER_2;
      addMessage(ERR_TRIGGER);

      // Other
  other;
      addMessage(ERR_UNKNOWN);
      return *On;
  endSl;

  return *Off;

 /end-Free
P                 E

Figure 9: Some other useful message procedures.


There You Have It

Hopefully these two articles have given you some food for thought. Remember that your RPG code does not have to be confined to RPG-only applications. All that great code can be opened up to wider use.


Paul Tuohy is CEO of ComCon, an iSeries consulting company, and is one of the co-founders of System i Developer, which hosts the RPG & DB2 Summit conferences. He is an award-winning speaker who also speaks regularly at COMMON conferences, and is the author of "Re-engineering RPG Legacy Applications," "The Programmers Guide to iSeries Navigator," and the self-study course called "iSeries Navigator for Programmers." Send your questions or comments for Paul to Ted Holt via the IT Jungle Contact page.


RELATED STORY

Getting the Message, Part 1



                     Post this story to del.icio.us
               Post this story to Digg
    Post this story to Slashdot


Sponsored By
HALCYON SOFTWARE

Learn how automated solutions for IBM i servers can
significantly reduce your operating costs. And understand
how powerful monitoring and automation techniques can
solve your business challenges.

Find out if automated operations solutions for IBM i,
i5/OS makes perfect business sense for your organization.

Learn more


Senior Technical Editor: Ted Holt
Technical Editor: Joe Hertvik
Contributing Technical Editors: Erwin Earley, Brian Kelly, Michael Sansoterra
Publisher and Advertising Director: Jenny Thomas
Advertising Sales Representative: Kim Reed
Contact the Editors: To contact anyone on the IT Jungle Team
Go to our contacts page and send us a message.

Sponsored Links

Infor:  Visit the first System i Virtual Conference hosted by Infor and IBM. View on-demand Webinar.
CCSS:  Need Pro-Active Management of Your IBM® i Server? We can help.
Patrick Townsend Security Solutions:  Get a customized state privacy law compliance report


 

IT Jungle Store Top Book Picks

Easy Steps to Internet Programming for AS/400, iSeries, and System i: List Price, $49.95
The iSeries Express Web Implementer's Guide: List Price, $49.95
The System i RPG & RPG IV Tutorial and Lab Exercises: List Price, $59.95
The System i Pocket RPG & RPG IV Guide: List Price, $69.95
The iSeries Pocket Database Guide: List Price, $59.00
The iSeries Pocket SQL Guide: List Price, $59.00
The iSeries Pocket Query Guide: List Price, $49.00
The iSeries Pocket WebFacing Primer: List Price, $39.00
Migrating to WebSphere Express for iSeries: List Price, $49.00
Getting Started With WebSphere Development Studio Client for iSeries: List Price, $89.00
Getting Started with WebSphere Express for iSeries: List Price, $49.00
Can the AS/400 Survive IBM?: List Price, $49.00
Chip Wars: List Price, $29.95


 
The Four Hundred
IBM Dynamic Infrastructure Announcements Due October 20

Steady as She Goes for IBM's Third Quarter

IBM i Access to Support Windows 7 on December 1

Mad Dog 21/21: Oy, Cloudy Us!

IBM Slashes i Compiler and Rational Tool Prices

Four Hundred Stuff
Jarman Flashes Clues on Future DB2 and RPG Directions

i365 Launches New EVault Backup Appliance, Cloud Storage Service

nuBridges Delivers Major Upgrade to MFT Solution

Info Builders Prophesizes World Series Winner with Predictive Analytics

Oracle Encourages JD Edwards Customers to Hang Tight

Four Hundred Monitor
Four Hundred Monitor's
Full iSeries Events Calendar

System i PTF Guide
October 17, 2009: Volume 11, Number 42

October 10, 2009: Volume 11, Number 41

October 3, 2009: Volume 11, Number 40

September 26, 2009: Volume 11, Number 39

September 19, 2009: Volume 11, Number 38

September 12, 2009: Volume 11, Number 37

September 5, 2009: Volume 11, Number 36

TPM at The Register
Gartner: IT spending growth next year

BMC eats Tideway for discovery tools

IBM installs temp server GM after insider trading furore

Boffins fawn over dirt cheap server clusters

Ellison whips out his Sparc TPC-C test

Sun tunes its VirtualBox

IBM, Intel execs arrested over alleged insider trading

US boffins use Obama dough to study clouds

IBM: Power7 to rollout throughout 2010

HP peddles app stress-testing cloud

IBM wrings more profits out of declining Q3

Oracle revs Xen VM to 2.2

THIS ISSUE SPONSORED BY:

Halcyon Software
ProData Computer Services
Twin Data


Printer Friendly Version


TABLE OF CONTENTS
Getting the Message, Part 2

Passing an Entire Result Set as a Parameter, Part 2

Setting Up SNTP Time Synchronization on an i5/OS Box

Four Hundred Guru

BACK ISSUES




 
Subscription Information:
You can unsubscribe, change your email address, or sign up for any of IT Jungle's free e-newsletters through our Web site at http://www.itjungle.com/sub/subscribe.html.

Copyright © 1996-2009 Guild Companies, Inc. All Rights Reserved.
Guild Companies, Inc., 50 Park Terrace East, Suite 8F, New York, NY 10034

Privacy Statement