On 31-Jul-2014 09:51 -0500, John Allen wrote:
We have a long running job that generates unnecessary
messages in the joblog.
So I am starting at the top and working my way through the
program and removing as many unnecessary messages from the
joblog as possible.
The program issues a
GRTOBJAUT OBJ(QTEMP/WORKFILEA) OBJTYPE(*FILE) USER(*PUBLIC)
AUT(*ALL)
  Side note: Unless that work file is /moved/ into a permanent library, 
that Grant Object Authority work is often for naught; i.e. the objects 
in the library QTEMP would not require any authorizations to be added 
outside of those for the group\owner, mostly because *PUBLIC will rarely 
have the opportunity or even ability [irrespective of authority] to 
access the object in the temporary library for the job.  Of course, 
perhaps the example is contrived and not representative of the work more 
generally, or the job will be operating under a switched user that 
either is not known until later or can not be known in advance, to whom 
authority might be granted specifically.
This generates 2 messages in the joblog:
CPI2201 "Authority given to user *PUBLIC for object WORKFILEA in
QTEMP object type *FILE."
  Notably, the program [message queue] to which that informational 
message CPI2201 was sent, is *not* the CL program issuing the Grant 
request.  That is the origin for the difficulty.  The program QSYGRAUT 
to which the message was sent, is no longer active on the stack, thus 
the Program Message Queue (PGMQ) of the system program QSYGRAUT is no 
longer accessible to the CL program that requested the GRTOBJAUT; that 
message is no longer addressable to any program by a _combination of_ 
both the named Program Message Queue and the specified Message Type. 
Note: The message is available by the Message Key, irrespective of the 
Program Message Queue; learning the value of the message-key is another 
story.
CPC2201 "Object authority granted."
  Notably, the program [message queue] to which that completion message 
CPC2201 was sent, *is* the CL program issuing the Grant request.  That 
is the reason the message _can be_ accessed; the program message queue 
where the message is located is the PGMQ() of that currently active 
program, and that program message queue is identified with the special 
value *SAME on RCVMSG and RMVMSG.
  Note: The F9=Display Message Details after F1=Help is chosen against 
a Message appearing in an [inter]active joblog will show to which 
program a message was sent, thus enabling learning whether the message 
will be available to the program that made the request for which the 
message was logged.  The spooled joblog will also reveal that same 
information.
Immediately after the GRTOBJAUT there is
CALL   PGM(QMHRMVPM) PARM('*' 0 ' ' '*NEW' ' ')
CALL   PGM(QMHRMVPM) PARM('*' 0 ' ' '*NEW' ' ')
  Warnings about the above CALL requests: the parameter definitions 
[documented for the API] do not match the data types of the data being 
passed [as literals] in the corresponding arguments.
<
http://www.ibm.com/support/knowledgecenter/api/content/ssw_ibm_i_71/apis/QMHRCVPM.htm>
  Warning 1: The 5th argument incorrectly informs the API that there is 
an unbelievably large amount of storage available in which to return 
exception data; i.e. the INPUT portion for the Error Code suggests that 
there were x'40404040' Bytes provided for the OUTPUT portion of that 
parameter; when in fact, a literal specification provides implicitly 
only 32-bytes of reserved storage, although as a literal, effectively 
there is no storage provided for return data per the parameter being 
inherently input-only to the invoked program.
  Warning 2: The 2nd parameter is functional, albeit deceptively, and 
possibly only accidentally.  Although the decimal value zero [shown as 
the digit 0 for the second argument in the above CALL requests] will be 
understood by the API to be the value x'00000000', the actual value 
being passed to the API for that parameter would actually be the value 
x'000000000000000F', the value of the PackedDecimal(15,5) with value of 
zero.  However, purposely coding in that manner to both coincidentally 
and somewhat deceptively achieve that effect, is discouraged.  Another 
programmer who does not understand how that result is achieved only with 
subterfuge might /copy/ that line of code, and then deciding a relative 
call stack entry should be /one/ for their purposes, they may think 
simply changing the decimal digit zero to the decimal digit one will 
suffice, but of course that change would do nothing; any of the 
legitimate possible _decimal values_ that would be functional [beyond 
the zero-value] would be nonsensical as compared with an appropriately 
specified binary\integer value, per having to increment by (N*100) 
instead of just increment solely by (N) to direct the API to the Nth 
relative Call Stack Entry.
  Additionally, the '*NEW' specification is a generic, such that *if* 
the invocation was able to do what was desired, then only one invocation 
would be required.  That is because after the first invocation, there 
would be no _new_ messages; well, unless the API was both allowed to and 
did, issue new message(s) to that call stack entry.
These two calls only remove message CPC2201 from the joblog,
message CPI2201 remains in the joblog
  Although not a helpful response, the effect is proper and expected, 
given what the messaging effects were from the request, with regards to 
where the messages are stored and how those messages are located.  The 
appropriate /corrections/ to the API invocation [those alluded earlier] 
would not change that effect.  The invocation removed the /new/ message 
from the specified program message queue, as requested.
  The special value '*' [asterisk] as the first argument indicates that 
the Program Message Queue (PGMQ) to be processed is the currently active 
program; "The message queue of the current call stack entry (that is, 
the call stack entry removing the messages)."  The value of zero as the 
second argument indicates the relative offset from the currently active 
program identified by the first argument; "Remove the messages from the 
message queue of the call stack entry specified in the Call stack entry 
parameter."  Any value specified for the fourth parameter except 
'*BYKEY' [which is a request to operate on the key specified in the 
third parameter] can act only against "messages in the call message 
queue"; together, the specifications in the first two arguments ask the 
API to process only the messages located within the program message 
queue of the active program as the requester of the Remove Message API. 
 But because [as noted earlier] the Information message CPC2201 is not 
in [was not sent to] the program message queue of that active program 
[which is where the API was asked to look, per the combination of values 
specified for the 1st and 2nd arguments], the info message can not be 
accessed using that invocation of the Remove Program Message API.
  Note: The duplicate API invocations are effectively the equivalent of:
      RMVMSG PGMQ(*SAME (*)) MSGQ(*PGMQ) CLEAR(*NEW) MSGKEY(*N)
  FWiW, while the value '*NEW' that was specified for the 4th argument 
signifies in terms of IBM i messaging the equivalent of not "*OLD" 
whereby '*OLD' signifies "already received\processed", and although the 
message CPI2201 was never _received_ such that the message should remain 
"*NEW", because the informational message is not in an\the active 
program message queue, the info msg is not available to a request to 
process the active program message queue PGMQ(*SAME (*)).  For the same 
reason, the specification of CLEAR(*ALL) also would not assist to remove 
those info messages.
   If the system program QSYGRAUT had sent [or moved] the informational 
message(s) to the invoker of the GRTOBJAUT, then the RMVMSG [or RCVMSG] 
or equivalent API invocation would have had little difficulty removing 
those messages along with the completion messages.  Unfortunately there 
is no parameter [nor an environment variable or anything else] that can 
be specified to influence\persuade the Grant request to /move/ those 
messages to the PGMQ of the requester :-(  Nothing that I am aware of is 
available, that would force the informational messages to be available 
[sent or moved] directly to the invoker, rather than being left in an 
inactive program message queue.
I tried replacing the two calls to QMHRMVPM with
RCVMSG     MSGTYPE(*LAST) RMV(*YES)
RCVMSG     MSGTYPE(*LAST) RMV(*YES)
With the same results
  FWiW: For a replacement, the command Remove Message (RMVMSG) might be 
expected instead, as described earlier; i.e. use of Receive Message 
(RCVMSG) as an effective /replacement/ for an API call might be 
considered more likely, had the previous API invocation been to the 
Receive Program Message (QMHRCVPM) API.
  No matter, the /same results/ [though actually they may not have 
been] is of no surprise, because the PGMQ(*SAME (*)) [as the default for 
RCVMSG (and RMVMSG)] has the same effects and limitations as the '*' and 
zero specifications on the Remove Message API [or similar RMVMSG request].
  As with the API invocations shown, the duplicate RCVMSG requests are 
also suspect.  In this case however, they are a potential issue; as 
contrasted with being simply redundancy of a generic request for the two 
API requests, the two RCVMSG requests might cause problems.  Those two 
CL command requests performed consecutively will either remove two 
messages [the completion and some prior message] or remove just the 
completion msg such that the second invocation would receive no message. 
 For lack of any return values [esp. on the second request], what or if 
a message was returned can not be easily determined within the CL 
program.  Having included a parameter specification like 
KEYVAR(&RTNKEY), the condition of (&RTNKEY *EQ '    ') indicates that no 
message was found using the specified criteria to locate a message; 
having included a parameter specification like MSGID(&MID), the value of 
&MID could be reviewed [in testing] to determine if the expected message 
identifier was being received.
What needs to be done to remove the message CPI2201 from the
joblog?
  The typical resolution is to use the technique [for which an example 
is provided in the documentation] to /remove inactive messages/ from the 
job message queue.  Various searches for such terms in the archives 
should reveal many past discussions with alternate examples and the link 
to the docs.  On a second try, but I do not recall the search tokens, I 
finally found the KnowledgeCenter link:
<
http://www.ibm.com/support/knowledgecenter/api/content/ssw_ibm_i_71/rbam6/rcvmsgpgm.htm>
  FWiW: The Remove Program Messages (QMHRMVPM) API does enable removing 
/all/ of the /inactive/ messages.  The /scope/ for the definition of 
/all/ however, is comprehensive; i.e. all of the messages within the 
entire _job message queue_ will be removed, not just those inactive 
since some particular request\invocation or some message key.  I do not 
recall ever using that invocation in my code due to the effects being so 
drastic; instead I have found usefulness only in the technique of 
obtaining a message key and then looping to remove successive messages 
by key, to remove the inactive messages since a particular message key.
  There are probably few scenarios whereby the extreme effect of 
removing _all inactive messages_ from the job message queue will be 
desirable.  Nonetheless, here is a simple CL invocation using the Remove 
Message API [with appropriately data typed literal values for the 
arguments, matched to the parameter definitions; though CLP variables 
often are good\better choices for passing the values]:
    call qsys/qmhrmvpm ('*ALLINACT ' x'00000000' +
                 '    ' '*ALL      ' x'0000000800000000')
  What should be the equivalent request, and much simpler coded within 
a CLP, is the following Remove Message (RMVMSG) request:
    rmvmsg pgmq(*allinact) msgq(*pgmq) clear(*all)
 
As an Amazon Associate we earn from qualifying purchases.