23
Mon, Dec
1 New Articles

How to Forward Messages in CL

CL
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Surely, you have noticed that each time you run a command incorrectly, OS/400 gets back to you with an error message of some sort. For example, you may want to display the contents of library XYZ with the Display Library (DSPLIB) command, as follows:

DSPLIB XYZ

If you don't have a library named XYZ, however, OS/400 tells you "Library XYZ not found" via a programmessage. In fact, most OS/400 activities produce messages of some sort, and not just for errorconditions. There are many types of messages, but the four types that stand out are informational(*INFO), completion (*COMP), diagnostic (*DIAG), and escape (*ESCAPE). In many cases, these messagescarry important information.

The problem is that, whereas running DSPLIB from the command line makes any error message immediatelyvisible, running the same command from a CL program hides the message. The reason is that OS/400 sendsthe message to the caller of the program that contains the command in error; if you run the commandfrom the command line, the command's CPP sends the error message to its caller (the command line). Ifyou run the command from a CL program, it's the CL program, not the command line, that gets themessage.

It's easy to see, therefore, the importance of forwarding messages up the call stack. In this article,I present a utility command that makes forwarding such program messages very simple and convenient.The call stack is a last-in, first-out queue (or "stack") that contains an ordered list of all theprograms that a particular job is running. At the top of the call stack, you'll find program QSYS/QCMDin most jobs. Then, as programs are called, they are added at the bottom of the stack.

Figure 1 shows a typical call stack. Let's suppose you're running an interactive job. Call stack level1 contains QCMD. Then, you call OPM program A directly from the command line; at that point, the callstack contains program A in level 2. Program A then calls ILE program B, which contains two procedures-procedure 1 and procedure 2, in such a way that procedure 1 calls procedure 2. Finally, procedure 2calls OPM program C.

When any of these programs and procedures ends in error, the error message must be forwarded up thecall stack so that the caller becomes aware of the error condition and can take corrective action (ormerely pass the bucket further up the call stack). For example, if OPM program C ends in error, ILEprocedure 2 gets the error message. The procedure can then either take corrective action or forward theerror message to its caller, ILE procedure 1.

The problem, then, is what procedure 1 should do about it. If it cannot take corrective action, itshould forward the message to its own caller. But its caller is OPM program A, which is two levels upthe stack; OS/400 inserted a procedure of its own, called _CL_PEP, inserted by the CL compiler. Thename stands for "CL Program Entry Procedure," and all ILE CL programs have one.

Over the years, I've seen dozens of variations of message-forwarding algorithms. The simplest involvesa Receive Message (RCVMSG) command and a Send Program Message (SNDPGMMSG) command, which forward to thecaller the escape message with which a CL program has ended in error. Although simple to code, it's adeficient technique, because often the escape message doesn't contain enough information to find outwhat went wrong.

A better technique involves the use of two APIs: QMHMOVPM and QMHRSNEM. The first API moves to thecaller any diagnostic messages, and the second API resends the escape message. This technique isbetter, but coding an API call is not something you can do with your eyes closed.

These difficulties led me to develop the Forward Program Messages (FWDPGMMSG) command (Figure 2). Its purpose is to facilitate coding the message-forwarding algorithm as much as possible:

  • You code only two lines of code.
  • You don't need any variables, so you can't forget any Declare (DCL) commands.
  • You always code the same two lines, regardless of CL program type (OPM or ILE).FWDPGMMSG has only two parameters, both of which accept default values:o Message type (MSGTYPE). With this parameter, you tell FWD-PGMMSG which messages you want to forwardto the caller. There are only three valid values: *DIAGESC (default), *INFOCOMP, and *ALL. *DIAGESCforwards diagnostic and escape messages only; *INFOCOMP, informational and completion messages only;*ALL, all four message types.
  • Convert escape to diagnostic (CVTESCAPE). Sometimes, you want to forward messages to the caller insuch a way that any escape messages don't interfere with the smooth operation of the caller. To do that, you must convert any escape messages to diagnostic while forwarding. FWDPGMMSG makes this easy ifyou specify *YES for this parameter. Or you can leave the default value, *NO, if you want escapemessages to remain as escape messages.

FWDPGMMSG can be used in any CL program or CL procedure. In most cases, you'll want to forward errormessages to the caller, so you can take the default values for both parameters and write your CL sourcecode as illustrated in Figure 3.

First, your CL program or procedure must contain a global Monitor Message (MONMSG) command, branchingcontrol to tag ERROR if any unexpected CPF, MCH, CEE, or other messages show up. You should at leastmonitor for CPF messages, but think of any others that may pop up. As usual, the global MONMSG must sitbetween the DCLs and the normal processing (or "body") of the program. Second, code a RETURN command atthe end of the body of the program, immediately followed by label ERROR, which in turn should befollowed by the message-forwarding mechanism. Finally, code a FWDPGMMSG with no parameters and a MONMSGto monitor CPF0000 (any CPF messages).

FWDPGMMSG without parameters will forward any diagnostic and escape messages to the CL program'scaller, without converting the escape message to diagnostic. The caller can then monitor for errormessages immediately after the CALL (or CALLPRC) command and take corrective action-or forward themessages further up the call stack.

It's very important that you code a MONMSG for CPF0000 immediately after FWDPGMMSG. If FWDPGMMSG wereto fail for any reason and you had omitted this MONMSG, your CL program would enter an infinite loop.The reason is simple: If FWDPGMMSG fails and there's no MONMSG, the global MONMSG traps it-and branchescontrol to tag ERROR, where it finds FWDPGMMSG again, failing again, and repeating this process in arather tight and CPU-intensive loop. The only way out would be to press SysRq and select option 2 torun the End Request (ENDRQS) command. If the job is running in batch, you'd have to run the End Job(ENDJOB) command from a display station.

FWDPGMMSG's processing program is PGM007CL (Figure 4). It may look complicated at first glance, butit's really not that difficult to follow. To begin with, you must realize that the CPP of a command yourun within a CL program or procedure is one level below the CL program or procedure. For example,suppose that, in Figure 1, ILE procedure 1 runs the FWDPGMMSG command. The command's CPP, PGM007CL,takes up the level right below ILE procedure 1.

With this in mind, it's clear that PGM007CL must consider the program immediately above it as thesource and the one above that as the target. So, FWDPGMMSG executed from ILE procedure 1 must forwardmessages from that procedure to the caller.

But, from the call stack diagram, ILE procedure 1's caller is none other than _CL_PEP. If PGM007CL wereto send the ILE procedure 1's messages there, they would fall into a black hole and never emerge.Clearly, then, PGM007CL must determine if, at the time it is running, _CL_PEP occupies a position twolevels up the call stack. If that is the case, PGM007CL must forward the messages three levels up thestack so it reaches OPM program A.

That's the first thing PGM007CL does. It uses API QMHSNDPM to send a dummy informational message twolevels up, obtaining the 4-byte key to the message in CL variable &MSGKEY. Then, it receives the samemessage by calling QMHRCVPM, retrieving the message information in variable &RCVVAR, which is 512 byteslong and structured according to data format RCVM0300. In this format, bytes 492 to 501 of &RCVVARcontain the name of the program or procedure from which the message is being received. All I have todo, then, is compare those bytes against _CL_PEP; if equal, I set to 3 the number of levels to movemessages up; otherwise, I set that number to 2.

Next, I determine whether PGM007CL must convert escape messages to diagnostic. If not, I need to calltwo APIs: QMHMOVPM to move program messages from one program to another, and then QMHRSNPM to resendthe escape message as another escape message. Otherwise (if PGM007CL must convert escape messages todiagnostic), I call only QMHMOVPM. One of the abilities of this API is that it automatically convertsescape messages to diagnostic.

Let's look at the parameters I have coded for each of these API calls.When moving program messages without conversion, QMHMOVPM gets the following parameters:

  1. Four blanks for the message key. I don't supply a message key, because I want QMHMOVPM to move more than one message and to do so by message type.
  2. An array of message type codes, which has been set to an appropriate value beforehand. For instance,if you specified MSGTYPE(*INFOCOMP), the array will contain *INFO in the first 10 bytes and *COMP inthe following 10. The remaining 20 bytes will be blank.
  3. A 4-byte binary number that indicates how many message types I am providing in parameter 2.
  4. An asterisk, which indicates that the target program is based on the program that called the API(PGM007CL).
  5. Another 4-byte binary number, which indicates how many levels up to go, starting from PGM007CL.Previously, I have set this number to 2 or 3, depending on _CL_PEP.
  6. The API error code structure, preset to nulls.
  7. A number 1 expressed in binary, indicating the length of parameter 4.
  8. Two qualifiers for the target call stack level, both of which must be *NONE.
  9. The string "*CHAR," to indicate that parameter 4 contains a character value instead of a pointer.
  10. An asterisk, to indicate that the source program is based on the program that called the API(PGM007CL).
  11. A number 1 expressed in binary, indicating that the source program is one level up from PGM007CL(i.e., PGM007CL's caller).

If the message types you selected for forwarding are either *DIAGESC or *ALL and no conversion is desired for escape messages, you need to call QMHRSNEM to resend the escape message up the call stack. There are seven parameters:

  1. Four blanks for the message key (you don't know the key to the escape message).
  2. The API error code structure, preset to nulls.
  3. A data structure organized according to data format RSNM0100, which is 38 bytes long. This structuretells the API where to send the escape message. The components of the data structure are (a) the numberof levels to go up the stack, either 2 or 3; (b) the target call stack entry qualifiers, both set to*NONE; (c) the number 10 in binary, to indicate the length of the next component; (d) an asterisk, toindicate that the target program is based on the program that called the API (PGM007CL).
  4. The number 38 in binary, which is the length of the data structure given in parameter 3.
  5. The string RSNM0100, which identifies the organization of the structure.
  6. An asterisk, to indicate that the source program is based on PGM007CL.
  7. The number 1 in binary, to indicate that the source program is PGM007CL's caller.

I call QMHMOVPM again in the second part of PGM007CL in order to move program messages when escapemessage conversion is desired. The parameters are nearly identical, however, so I won't belabor thepoint. And before I close, I'd like to mention panel group PGM007PG (Figure 5), which provides helptext for the FWD-PGMMSG command.

Writing FWDPGMMSG was a challenge, because I had little experience with message manipulation in ILE.Besides, the IBM manuals did not mention _CL_PEP's visibility to the APIs (it is invisible to both SNDPGMMSG and RCVMSG, however), so I spent a lot of time trying different things, more or less hit andmiss, until I got the idea to insert a DSPJOB OPTION(*PGMSTK) here and there to watch the call stack atwork.

In any case, FWDPGMMSG is here. I believe you'll find it an invaluable resource for your CL programs,especially since you do not have to concern yourself over the nature (OPM or ILE) of the programs youare writing. And if you have any suggestions for improvement, I'd love to hear them.

Ernie Malaga is a technical editor for Midrange Computing. He may be reached by fax at 305-387-8263 or
by email at This email address is being protected from spambots. You need JavaScript enabled to view it..

OS/400 CL Programming V3R6 (SC41-4721, CD-ROM QBJAUO00)
OS/400 Message Handling APIs (SC41-4862, CD-ROM QBAKAMN01) /*===================================================================*/

/* To compile: */
/* */
/* CRTCMD CMD(XXX/FWDPGMMSG) PGM(XXX/PGM007CL) + */
/* SRCFILE(XXX/QCMDSRC) TEXT('Forward + */
/* Program Messages') ALLOW(*IPGM *IMOD + */
/* *BPGM *BMOD) HLPPNLGRP(XXX/PGM007PG) + */
/* HLPID(*CMD) */

Figure 1: Typical call stack structure

Figure 2: Command FWDPGMMSG

How_to_Forward_Messages_in_CL03-00.png 475x345

/* */
/*===================================================================*/

CMD PROMPT('Forward Program Messages')

PARM KWD(MSGTYPE) TYPE(*CHAR) LEN(9) RSTD(*YES) +

DFT(*DIAGESC) VALUES(*DIAGESC *INFOCOMP +
*ALL) PROMPT('Message types')

PARM KWD(CVTESCAPE) TYPE(*CHAR) LEN(4) RSTD(*YES) +

DFT(*NO) VALUES(*YES *NO) PROMPT('Convert +
*ESCAPE to *DIAG')

Figure 3: Using FWDPGMMSG

PGM

DCL VAR(...)
...

MONMSG MSGID(CPF0000 MCH0000) EXEC(GOTO CMDLBL(ERROR))

/* Normal processing */

RETURN

ERROR:

FWDPGMMSG
MONMSG MSGID(CPF0000)

ENDPGM /*===================================================================*/

/* To compile: */
/* */
/* CRTCLPGM PGM(XXX/PGM007CL) SRCFILE(XXX/QCLSRC) + */
/* TEXT('CPP for FWDPGMMSG command') */
/* */
/*===================================================================*/

PGM PARM(&MSGTYPE &CVTESCAPE)

DCL VAR(&APIERRCDE) TYPE(*CHAR) LEN(8) +

VALUE(X'0000000000000000')
DCL VAR(&CALLER2) TYPE(*CHAR) LEN(10)
DCL VAR(&CVTESCAPE) TYPE(*CHAR) LEN(4)
DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4)
DCL VAR(&MSGTYPE) TYPE(*CHAR) LEN(9)
DCL VAR(&MSGTYPEARR) TYPE(*CHAR) LEN(40)
DCL VAR(&NBRMSGTYPE) TYPE(*CHAR) LEN(4)
DCL VAR(&RCVVAR) TYPE(*CHAR) LEN(512)
DCL VAR(&STRUCT) TYPE(*CHAR) LEN(38)

DCL VAR(&TOPGMQ) TYPE(*CHAR) LEN(10) VALUE('*')
DCL VAR(&TOPGMQLEN) TYPE(*CHAR) LEN(4) +

VALUE(X'0000000A') /* Dec = 10 */
DCL VAR(&TOPGMQCTR) TYPE(*CHAR) LEN(4)
DCL VAR(&TOPGMQQUAL) TYPE(*CHAR) LEN(20) +

VALUE('*NONE *NONE')

/* Bridge over _CL_PEP if present */
CALL PGM(QMHSNDPM)PARM('''''Dummy'+

X'00000005' '*INFO' '*' X'00000002' +
&MSGKEY &APIERRCDE)

CALL PGM(QMHRCVPM) PARM(&RCVVAR X'00000200' +

'RCVM0300' '*' X'00000002' '*ANY' &MSGKEY +
X'00000000' '*REMOVE' &APIERRCDE)

CHGVAR VAR(&CALLER2) VALUE(%SST(&RCVVAR 492 10))

IF COND(&CALLER2 *EQ '_CL_PEP') THEN(DO)

CHGVAR VAR(%BIN(&TOPGMQCTR)) VALUE(3)
ENDDO
ELSE CMD(DO)

CHGVAR VAR(%BIN(&TOPGMQCTR)) VALUE(2)

Figure 4: CL program PGM007CL

ENDDO

/* Requested to preserve *ESCAPE messages */
IF COND(&CVTESCAPE *EQ '*NO') THEN(DO)

/* Move program messages */
IF COND(&MSGTYPE *EQ '*DIAGESC') THEN(DO)

CHGVAR VAR(&MSGTYPEARR) VALUE('*DIAG')
CHGVAR VAR(%BIN(&NBRMSGTYPE)) VALUE(1)
ENDDO
ELSE CMD(IF COND(&MSGTYPE *EQ '*INFOCOMP') THEN(DO))

CHGVAR VAR(&MSGTYPEARR) VALUE('*INFO *COMP')
CHGVAR VAR(%BIN(&NBRMSGTYPE)) VALUE(2)

ENDDO
ELSE CMD(IF COND(&MSGTYPE *EQ '*ALL') THEN(DO))

CHGVAR VAR(&MSGTYPEARR) VALUE('*INFO *COMP +

*DIAG')
CHGVAR VAR(%BIN(&NBRMSGTYPE)) VALUE(3)
ENDDO
CALL PGM(QMHMOVPM) PARM(' ' &MSGTYPEARR +

&NBRMSGTYPE '*' &TOPGMQCTR &APIERRCDE +
X'00000001' '*NONE *NONE' '*CHAR' '*' +
X'00000001')

MONMSG MSGID(CPF0000)

/* Resend escape message */
IF COND(&MSGTYPE *EQ '*DIAGESC' *OR &MSGTYPE +

*EQ '*ALL') THEN(DO)
CHGVAR VAR(&STRUCT) VALUE(&TOPGMQCTR *CAT +

&TOPGMQQUAL *CAT &TOPGMQLEN *CAT &TOPGMQ)
CALL PGM(QMHRSNEM) PARM(' ' &APIERRCDE &STRUCT +

X'00000026' 'RSNM0100' '*' X'00000001')
MONMSG MSGID(CPF0000)

ENDDO
ENDDO

/* Requested to convert *ESCAPE messages */
ELSE CMD(DO)

IF COND(&MSGTYPE *EQ '*DIAGESC') THEN(DO)

CHGVAR VAR(&MSGTYPEARR) VALUE('*DIAG *ESCAPE')
CHGVAR VAR(%BIN(&NBRMSGTYPE)) VALUE(2)

ENDDO
ELSE CMD(IF COND(&MSGTYPE *EQ '*INFOCOMP') THEN(DO))

CHGVAR VAR(&MSGTYPEARR) VALUE('*INFO *COMP')
CHGVAR VAR(%BIN(&NBRMSGTYPE)) VALUE(2)

ENDDO
ELSE CMD(IF COND(&MSGTYPE *EQ '*ALL') THEN(DO))

CHGVAR VAR(&MSGTYPEARR) VALUE('*DIAG *ESCAPE +

*INFO *COMP')
CHGVAR VAR(%BIN(&NBRMSGTYPE)) VALUE(4)
ENDDO
CALL PGM(QMHMOVPM) PARM(' ' &MSGTYPEARR +

&NBRMSGTYPE '*' &TOPGMQCTR &APIERRCDE +
X'00000001' '*NONE *NONE' '*CHAR' '*' +
X'00000001')

MONMSG MSGID(CPF0000)
ENDDO

ENDPGM
.*=====================================================================

.* To compile:
.*

.* CRTPNLGRP PNLGRP(XXX/PGM007PG) SRCFILE(XXX/QPNLSRC) +
.* TEXT('Help text for FWDPGMMSG command')
.*

.*=====================================================================

:PNLGRP.

:HELP NAME=fwdpgmmsg.
Forward Program Messages (FWDPGMMSG)
:P.

FWDPGMMSG forwards up the call stack messages of type *INFO, *COMP,

Figure 5: Panel group PGM007PG

*DIAG, and *ESCAPE. Optionally, *ESCAPE messages may be converted
to *DIAG.
:EHELP.

.*=====================================================================

:HELP NAME='fwdpgmmsg/msgtype'.
Message type (MSGTYPE) Parameter
:XH3.Message type (MSGTYPE) Parameter
:P.

Enter a special value that describes the kind of messages you want
to forward up the call stack. The valid values are&colon.
:P.
:PARML.
:PT.:PK DEF.*DIAGESC:EPK.
:PD.

Forward *DIAG (diagnostic) and *ESCAPE (escape) messages only.
:PT.*INFOCOP
:PD.

Forward *INFO (informational) and *COMP (completion) messages only.
:PT.*ALL
:PD.

Forward *INFO, *COMP, *DIAG, and *ESCAPE.
:EPARML.
:EHELP.

.*=====================================================================

:HELP NAME='fwdpgmmsg/cvtescape'.
Convert escape (CVTESCAPE) Parameter
:XH3.Convert escape (CVTESCAPE) Parameter
:P.

Enter an option that describes whether you want to convert *ESCAPE
messages to *DIAG, or leave them as *ESCAPE. The valid values
are&colon.
:P.
:PARML.
:PT.:PK DEF.*NO:EPK.
:PD.

Do not convert *ESCAPE messages to *DIAG.
:PT.*YES
:PD.

Convert *ESCAPE messages to *DIAG.
:EPARML.
:EHELP.

.*=====================================================================

:EPNLGRP.

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: