With the advent of some great third-party tools for web-enabling RPG IV, RPG IV for the Web applications are being written in nearly every System i shop today. The green screen, while still (sadly) the dominant user interface for System i, is losing ground to browser-based user interfaces almost as quickly as NetScape Navigator lost market share to Internet Explorer and, today, almost as fast as Internet Explorer is losing market share to Firefox.
This article is not about writing browser-based user interfaces and connecting them to RPG IV; instead, what I want to illustrate is a technique that's somewhat common in traditional 5250 user interfaces -- retrieve the device ID in RPG and using it in your program.
First we have to remember that the connection between the web browser and the RPG IV program is not the same as that of the 5250/DDS-based display file and RPG IV. The connection with the browser is via TCP/IP through the Apache Web Server. If you attempt to retrieve the display device name via the PSDS data structure (the traditional method), you might end up with a PSDS that looks something like the following:
D PSDS SDS
D CPFMsgID 7A Overlay(PSDS:40)
D JobId 26A Overlay(PSDS:264)
D JobNbr 6A Overlay(JobId:1)
D JobName 10A Overlay(JobId:*NEXT)
D JobUser 10A Overlay(JobId:*NEXT)
The JOBNAME subfield normally contains DSP01 or whatever the device name is for the interactive job. This is due to a standardized naming scheme for interactive jobs; the jobs adapt the display device name as the job name. This made it very convenient to know which device was used your program. But what happens if you use this technique with a web job? Every user will have the same job name. This is just the way it works with threads and the HTTP web server.
Suppose you're running an RPG IV program connected to a web page. Now you want to deliver something to that web user based on something they requested. Normally you would write that information out to the standard output device using the QtmhWrtStout API. This could be anything from another HTML web page, a PDF file, a CSV-based Excel document, or just about anything else. But what if you have a sophisticated application that needs to open a connection to that web browser? What if you want to, for example, open a SOCKET connection, or perhaps open an FTP session to that device in order to send them a file?
| RPG TnT: 101 Dynamic Tips 'n Techniques with RPG IV by Bob Cozzi is available now. | |
[2] |
The latest book from author Bob Cozzi is 300 pages containing 101 examples RPG IV Tips and Techniques for everyday programming tasks, from date calculations, to regular express searches, to using APIs. Cozzi wrote down every cool technique he's found over the years, updated them, and consolidated them into this compact book that is a great desktop companion, and it includes full example source code. Order your copy today. [3] |
To accomplish this, you need to retrieve the IP address of the web user. To retrieve the IP address, requires two things:
To use the getenv C runtime function, you need two things:
To specify the QC2LE binding directory, use the BNDDIR keyword on the Header specification, as follows:
H BNDDIR('QC2LE')
Please note that QC2LE is specified in all upper case and is quoted. The biggest mistake RPG IV programmers make with regards to adding this binding directory keyword to their code, is specifying it in all lower case. For example, BNDDIR('qc2le') does not work; it must be all upper case, BNDDIR('QC2LE'). Specifying bnddir('QC2LE') is also valid, but the binding directory name must be all upper case.
The prototype for the getenv API is fairly simple. It contains only one parameter, as follows:
D getenv PR * extProc('getenv')
D envVar * Value Options(*STRING)
Note that in this situation, the name of the subprocedure being called is a C runtime function that is named with all lowercase letters. This must be specified on the EXTPROC keyword, quoted, and in all lower case. So the tip here is quoted values in RPG IV are in fact case-sensitive. Unquoted text in RPG IV source is converted to all upper case by the compiler.
The simplest and briefest way to describe the environment to RPG programmers is to equate it to the LDA. But since a growing number of RPG IV programmers have never used the LDA, I'll go into a bit more detail.
The environment is a Unix thing. It is a storage area automatically created by the system for each job that is run. Programmers may store and retrieve information in the environment using an identifier. This identifier is sort of like the key or index to the information. For example, if you store the value "NEXTINVOICE=3741" in the environment, you can retrieve the value for it by using the getenv function, and specifying getenv('NEXTINVOICE'), or more precisely:
D nextInv S 10A
/free
nextInv = %str(getenv('NEXTINVOICE'));
/endif
All environment values are stored and returned in text/character format. You can't really store a packed decimal value and retrieve a packed decimal value in the environment. Why? Because Unix doesn't have packed decimal data types.
When a value is returned from the environment using getenv, a pointer to the data associated with the environment variable name is returned. To copy the data at that pointer location to an RPG IV variable, you need to wrap the getenv function in RPG IV's %STR built-in function. This does the conversion from "pointer to string" to character field. Of course, it would be easier if IBM would simply do this conversion automatically for us, or give us a keyword on return values on prototypes that did this, but I digress...
Back to the web user's IP address. How does knowing all this help us get the IP address of our web users?
Not only can you add and retrieve things from the environment, so can those who have written applications that are running on your System i. This includes IBM and third-party programmers. In fact, the Apache web server loads the environment with lots of information for each web transaction. But the environment variable we're most interested in for our purposes is REMOTE_ADDR (remote IP address).
The REMOTE_ADDR environment variable is set to the IP address of the web user by the web server. By the time your RPG IV program receives control, the IP address is already stored in the environment. So retrieving it into your RPG IV program is simply a matter of including the BNDDIR('QC2LE') keyword and the getenv prototype, and then issuing the following RPG IV statement:
D userAddr S 15A
/free
userAddr = %str(getenv('REMOTE_ADDR'));
/endif
The IP address is stored in the USERADDR field in the format 192.168.1.101 and will be left-justified. At this point, you can now use FTP or a custom SOCKETs program to open a connection to the web user's system.
To be concise, I've consolidated this technique for you in the following example:
H BNDDIR('QC2LE')
D getenv PR * extProc('getenv')
D envVar * Value Options(*STRING)
D userAddr S 15A
/free
userAddr = %str(getenv('REMOTE_ADDR'));
/endif
Bob Cozzi recently produced the RPG World, [4] his annual conference for RPG IV programmers and managers. This summer, Bob is holding RPG World Academy, a series of one-day seminars for RPG IV programmers; the first in this series is Subprocedures and Service Programs, and the second is RPG IV for the Web. RPG World Academy is being held in cities throughout North America during the summer of 2008. Visit www.RPGWorld.com [5] and click the Academy link for more details, dates, and locations.
Links:
[1] http://systeminetwork.com/author/bob-cozzi
[2] http://www.amazon.com/dp/1583041214?tag=rw0c-20&camp=14573&creative=327641&linkCode=as1&creativeASIN=1583041214&adid=0FDDKRAMASMW6SZB3462&
[3] http://www.amazon.com/dp/1583041214?tag=rw0c-20&camp=14573&creative=327641&linkCode=as1&creativeASIN=1583041214&adid=0FDDKRAMASMW6SZB3462&
[4] http://www.rpgworld.com
[5] http://www.RPGWorld.com