You are on page 1of 15

AJAX for RPG Web Programs

Asynchronous JavaScript* and XML (AJAX) feels like another great step toward the promised land of
user interfaces for Web developers who didn’t learn to program on character-based displays. Its ease of
use flows like Mountain Dew down a parched throat, and productivity abounds like a Super Size fry
spilling over on your tray at McDonald’s. I don’t want to take away from AJAX’s time in the spotlight,
but it’s disheartening to see something that declares a better user experience without recognizing that’s
been around for decades with character-based systems like RPG. Sure, we don’t have graphics or
“Punch the monkey and win $100” banner ads on our green-screen apps, but darn it, we get’r done
when it comes to business applications. With that said, I’m a proponent for GUI; I just wish IBM would
come out with one that didn’t have Java* intermingled.
The reason I say AJAX is repeating what we’ve had with RPG on the IBM* System i* platform for
years is because the Web has been steadily replacing traditional business green screens over the past 10
years and has had inevitable growth pains trying to offer a useful, productive user experience.
Eventually, we’ll get to a place where the browser works exactly like a green screen (only prettier).
Apart from the full circle and the hype surrounding AJAX, its possibilities really are quite intriguing.
Seeing full-fledged products like Google Docs (http://docs.google.com) showing off AJAX’s muscle
signals that it’s only a matter of time before more Web-site authors embrace the technology.
In this article, I intend to keep RPG programmers in the loop and away from the french-fry machines at
their local burger joints by providing a useful example of how to employ AJAX when building a simple
Web form. This will be in conjunction with a straightforward RPG CGI program doing validation
against a DB2* database. But first things first: What is AJAX?

What is AJAX?
AJAX is a collection of technologies—JavaScript, cascading style sheets (CSS) and the HTML
document object model (DOM)—used to make program calls from the browser to a server-side
resource (e.g., an RPG CGI program) and use the server’s response to modify the contents of what the
user sees on the Web page. What makes this special is when it’s used to do incremental changes to the
Web page versus doing a full refresh, which requires the browser to re-render information that hasn’t
changed. When the browser only has to update portions of the page it’s obviously processing less and,
by getting rid of that split second where the browser goes white, improving the user experience with
less waiting and more productivity. Let’s examine an example to relate AJAX to a day in the life of an
RPG green-screen programmer.
In green-screen programming, it’s necessary to validate user input to ensure, for example, that valid
item numbers are being placed in the order-entry physical files. If a CHAIN produces a “not found”
when a user enters item number “XYZ,” that must be relayed back to the user. The process of telling
the user an error occurred usually means an attribute of a field is going to have an indicator turned *ON
to change the color of that field or highlight it to make it jump off the page. That’s exactly what we’ll
accomplish in the following AJAX example, except we’ll operate in a browser instead of a terminal
emulator, and instead of DDS field definitions and indicators we’ll have HTML <input/> fields. HTML
has no indicators or DDS field keywords. Instead we’ll use CSS to alter a field’s appearance when an
invalid entry is made.
Code Sample 1 is the HTML form into which the user will enter an item number. It’ll look like Figure 1
when displayed in the browser. Viewing the HTML behind this page reveals a form named “form1”
with two fields—“fld_itm” and “fld_anotherfield.” The objective is to change the border of “fld_itm”
to red when an invalid item number is entered (see Figure 2). To do that, a call will be made to a
System i RPG program to see whether the item entered is valid. The “onblur” attribute indicated that
the call to the System i platform will be made when the user exits the field. When the user exits
“fld_itm” the “vldItm” JavaScript function will be called. This is really no different from calling an
RPG program from the command line and passing in literal parameters to it. The value that’s in single
quotes is what’s being passed to function “vldItm.” In this case it’s the same value in the ID attribute of
this input tag. More about that parm being passed later. The “fld_anotherfield” field simply exists so we
have someplace to tab to.
The final thing to note is the class attributes value, which is set to “input.” This is telling the field that it
should look for a CSS named “input” and apply it to itself. If the field’s in error, we’ll use JavaScript to
dynamically change the class-attributes value from “input” to “input_error.”
Code Sample 2 shows the CSS definition used in this example. I won’t go into syntax but will point out
that the third parameter for the “border:” definition is different for “input” versus “input_error.” The
value #999999 is the hex value for a grayish color, and #FF0033 is red. These are the two different
style sheets that’ll be used to alter the appearance of field “fld_itm.”
Testing the VLDITM RPG program can be done easily from the browser by composing a
URL emulating exactly what the JavaScript is concatenating.

Process Lifecycle Steps


Now let’s go through the process lifecycle step by step to gain a more defined understanding of where
each technology is used.
Step 1: First, the user brings up a page residing on your System i Web server. For this example, we’ll
use the following URL: http://YourSystemiIP/ajax.html (or live on the Internet: http://red.rpg-
xml.com/ajax.html). The file ajax.html is a static file residing on the IFS in the root of the Web server
(i.e. /www/mywebserver/htdocs/ajax.html). The contents of ajax.html could just as easily have been
produced by an RPG program, but for simplicity’s sake weren’t. When the page loads, it’ll come across
the global declaration of the JavaScript variable named “http” as shown in Code Sample 3. This is
calling function crtReqObj() to obtain the request object, which will be used in later calls to the back-
end System i platform. As you can see, there’s an if statement checking whether this is a Microsoft* or
other browser, because Microsoft implemented the functionality differently from all other popular
browsers and uses its own method to obtain the XMLHttpRequest object. Once the request object is
placed into variable “http” the page is in a wait state until the user does something.
Step 2: The user enters an invalid item of “XYZ” into field “fld_itm” and tabs out of the field. Tabbing
out of the field causes the function specified in the onblur attribute to execute (vldItm in this case). The
name of the field is passed as an input parameter to vldItm because that value will eventually be passed
to the System i application and back to the browser to aid in knowing which field to update if the item
number is invalid. You could just set a global variable in the browser to retain the name of the field
currently being validated, but since this is an asynchronous call, we don’t lock the browser while the
validation request is being made. So if the user quickly tabbed through five fields that were being
validated we would need to make sure we knew which field was to be updated when each request came
asynchronously back from the server.
The first line of function vldItm in Code Sample 4 introduces us to the DOM. DOM is a representation
of an HTML file in memory that can be called upon to modify the contents of a Web page. In our
scenario, we’ll use it to obtain and set information for field “fld_itm.” The statement
document.getElementById(fldName) is attempting to get the field object by its ID within the DOM.
Once that’s obtained, we have access to all of the field’s different properties. The next statement,
http.open, will build the URL necessary to call the RPG program on the System i platform using the
field name and value as parameters. The first parameter of http.open lets us specify the method to use
in calling the server. In this case “get” was specified because URL variables are being used, but “post”
could also be used if body content was to be passed (i.e. a larger amount of data possibly wrapped in
XML). It would be good to note at this point that the request to the server doesn’t pass any XML, nor
does the server return XML. XML can be used to send and receive data, but for the sake of simplicity it
wasn’t used here.
The next line, http.onreadystatechange = vldItmResponse, declares what JavaScript function should
process the response from the server. This is similar to using the %PADDR (procedure address) BIF in
RPG to obtain the address of a subprocedure. Now that the http object has been opened and initialized,
the send can occur with statement “http.send(null).” If the body of the request contained data to send,
you would specify it here instead of passing null.
Step 3: At this point the RPG program VLDITM(*PGM) has been invoked on the System i Web server.
Code Sample 5 shows the RPG program reading the values from the URL (itm and fldName) and using
them to determine what should be returned to the browser. Instead of chaining to an external file, this
program will only have one valid item—“HAT.” If any other value is specified for the item, a value of
not found will be returned to the browser. In our scenario, two specific string values can be passed back
to the browser for processing: “fld_itm|FOUND” or “fld_itm|NOT_FOUND.” You could call this
vertical bar delimited data, but really any delimiter could be used. To make this a valid response for the
browser to consume, the content type must be specified followed by two new lines and then the
contents of variable outXml. All of that will then be pushed out to the browser by calling subprocedure
toBrowser. Note: getUrlVar and toBrowser are locally defined subprocedures included in the code
available online (www.ibmsystemsmag.com/zip/rpgajax.zip), as are the rest of the files necessary to run
this RPG AJAX example. After *INLR is turned *ON the RPG program’s portion is done and the
response now resides in the client’s browser waiting to be processed.
Testing the VLDITM RPG program can be done easily from the browser by composing a URL
emulating exactly what the JavaScript is concatenating. Below are some URLs that offer examples on
how to test the VLDITM RPG program. Note that you’ll need to replace 172.29.134.41 with the IP
address of your System i platform.
• http://172.29.134.41/cgi-bin/vlditm
• http://172.29.134.41/cgi-bin/vlditm?itm=hat&fldname=fld_itm
• http://172.29.134.41/cgi-bin/vlditm?itm=coat&fldname=fld_itm
Or, if you want to see it on a live server right now, head to the following URLs, which take you to my
System i platform on the Internet:
• http://red.rpg-xml.com/rxs/vlditm
• http://red.rpg-xml.com/rxs/vlditm?itm=HAT&fldName=fld_itm
• http://red.rpg-xml.com/rxs/vlditm? itm=COAT&fldName=fld_itm
Available Downloads:
Code Sample 7
To see an example of a System i Apache configuration file, direct your browser to my blog entry
regarding it (http://mowyourlawn.com/blog/?p=9).
Step 4: The server notifies the browser that the response is done and the first thing that must happen is
to verify that the response was successful by checking “http.readyState” and “http.status,” as shown in
Code Sample 6. A readyState of 4 means the process is complete from the JavaScript asynchronous
end. You’ll find a complete list of readyStates in “Useful References”. A status of 200 means the
System i Apache server considered the transaction a success. The next line declares the response
variable and fills it with the results from the server by accessing http.responseText.
The vertical delimiter line will be used to split the response into elements within an array for easy
accessibility. After the split, “cmp” is occupied with two elements. The first element, cmp[0], contains
the field name, in this case “fld_itm.” The second element, cmp[1] contains the value “NOT_FOUND”
or “FOUND.” The document.getElementById statement will retrieve the <input/> field named
“fld_itm” and place it in JavaScript variable “fld” so the class can be set to either “input_error” or
“input” based on the value in cmp[1].
Code Sample 7 gives an example of entering an invalid item number. Note that only the attributes of
the input field were changed—the entire page wasn’t rendered again.

Cool New Web Features


That’s it! The input from the user was successfully validated by the System i platform without hitting a
submit button. Now, this was a fairly simple approach to using the AJAX technologies for user-input
validation. One could make more use of the X in AJAX and pass XML into the server and receive
XML back for the response, but that wasn’t the goal of this example. The goal here was to show how a
simple RPG CGI application can still run with the big dogs in the cool new Web features arena. It all
comes down to plain, old text manipulation and a good Web server—and with Apache coupled with
RPG we have both.
On a final note, many open-source JavaScript code sets can be used in the browser to help facilitate
AJAX-style coding. A couple that I’ve used can be found on my System i platform on the Internet
(http://red.rpg-xml.com/rxs_ajax.html). This customer-maintenance program is taking RPG and AJAX
to a slightly higher level and is doing multiple transactions (load, new, save) without refreshing the
browser page. This goes to show that you could potentially write entire applications using AJAX
messaging back to RPG CGI programs.

Useful References
XMLHttpRequest Valid Responses
0 = Uninitialized
1 = Loading
2 = Loaded
3 = Interactive
4 = Complete
To see this application in action on a live System i platform, visit: http://red.rpg-xml.com/ajax.html.
For a deeper understanding of these concepts and tools, here are some useful links:
• http://en.wikipedia.org/wiki/ajax
• http://en.wikipedia.org/wiki/document_object_model
• http://en.wikipedia.org/wiki/cascading_style_sheets
• http://en.wikipedia.org/wiki/web_colors
• http://en.wikipedia.org/wiki/javascript
Binding directory CGI has the following entry in it:

Object Type Library Date Time


QZHBCGI *SRVPGM *LIBL 12/20/05 10:10:13

H bnddir('*LIBL/CGI') dftactgrp(*no)
D getUrlVar pr 32767A varying
D pCmdStr 300A value
D toBrowser pr extproc('QtmhWrStout')
D Data like(outXml)
D Length 10I 0 const
D Error like(gError)

D qzhbcgiparse pr extproc('QzhbCgiParse')
D cmdStr 300A
D outputFmt 8A
D tgtBuffer 32767A options(*varsize)
D tgtBufferLen 9B 0
D respLen 9B 0
D apiError LikeDS(gError)

D gError ds qualified
D bytesP 10I 0 INZ(56)
D bytesA 10I 0
D msgID 7
D reserverd 1
D data 40

D outXml s 256a
D itm s 20a
D fldName s 20a

/free
itm = getUrlVar('-value itm');
fldName = getUrlVar('-value fldName');
if itm = 'HAT';
outXml = %trim(fldName) + '|FOUND';
else;
outXml = %trim(fldName) + '|NOT_FOUND';
endif;

outXml = 'Content-type: text/plain' + x'1515' + outXml;


toBrowser(outXml : %len(%trim(outXml)) : gError);
*inlr = *on;
/end-free
//-----------------------------------------------------------------------
// @Author: Aaron Bartell
// @Modified:
//-----------------------------------------------------------------------
P getUrlVar B Export
D getUrlVar pi 32767A varying
D pCmdStr 300A value

D EOL C X'25'
D output S 32767A
D tgtBuf S 32767A
D outputFmt S 8A INZ('CGII0200')
D respLen S 9B 0
D tgtBufLen S 9B 0 INZ(%Size(TgtBuf))
D position S 5I 0

D error DS LikeDS(gError)
/Free

pCmdStr = %trim(pCmdStr) + X'00';


tgtBuf = '';
qzhbCgiParse(pCmdStr: outputFmt: tgtBuf: tgtBufLen: respLen: error);
if (%scan('-value':pCmdStr) > 0) And respLen > 0;
position = %scan(EOL: tgtBuf: 1);
if position < %size(tgtBuf) And position <> 0;
position += 1;
position = %scan(EOL: tgtBuf: position);
else;
position = 0;
endif;

if position = 0 And %scan(EOL: tgtBuf: 1) > 0;


respLen = respLen - 1;
endif;
endif;
if respLen > 0;
output = %subst(tgtBuf: 1: respLen);
else;
output = '';
endif;

return output;
/end-free
CODE SAMPLE 1
<form name="form1">

Enter item:
<input class="input" type="text" id="fld_itm" onblur="vldItm('fld_itm');" />

<br/>
<br/>

Another Field:
<input class="input" type="text" name="fld_anotherfield"/>
</form>

CODE SAMPLE 2
<style>
.input {
border: 2px solid #999999;
}
.input_error {
border: 2px solid #FF0033;
}
</style>

CODE SAMPLE 3
var http = crtReqObj();
function crtReqObj() {
var reqObj;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
reqObj = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
reqObj = new ActiveXObject("Microsoft.XMLHTTP");
}
return reqObj;
}

CODE SAMPLE 4
function vldItm(fldName) {
var fld = document.getElementById(fldName);

http.open('post',
'http://172.29.134.41/cgi-bin/vlditm?itm=' +
fld.value + "&fldName=" + fldName);
http.onreadystatechange = vldItmResponse;
http.send(null);
}

CODE SAMPLE 5
D outXml s 256a
D itm s 20a
D fldName s 20a
/free

itm = getUrlVar('-value itm');


fldName = getUrlVar('-value fldName');
if itm = 'HAT';
outXml = %trim(fldName) + '|FOUND';
else;
outXml = %trim(fldName) + '|NOT_FOUND';
endif;

outXml = 'Content-type: text/plain' + x'1515' + outXml;


toBrowser(outXml : %len(%trim(outXml)) : gError);

*inlr = *on;

/end-free

CODE SAMPLE 6
function vldItmResponse() {
if(http.readyState == 4 && http.status == 200){
var response = http.responseText;
var cmp = new Array();

cmp = response.split('|');

var fld = document.getElementById(cmp[0]);


if(cmp[1] == 'NOT_FOUND'){
fld.className='input_error';
} else{
fld.className='input';
}
}
}

CODE SAMPLE 7
Binding directory CGI has the following entry in it:

Object Type Library Date Time

QZHBCGI *SRVPGM *LIBL 12/20/05 10:10:13

H bnddir('*LIBL/CGI') dftactgrp(*no)

D getUrlVar pr 32767A varying

D pCmdStr 300A value

D toBrowser pr extproc('QtmhWrStout')

D Data like(outXml)
D Length 10I 0 const

D Error like(gError)

D qzhbcgiparse pr extproc('QzhbCgiParse')

D cmdStr 300A

D outputFmt 8A

D tgtBuffer 32767A options(*varsize)

D tgtBufferLen 9B 0

D respLen 9B 0

D apiError LikeDS(gError)

D gError ds qualified

D bytesP 10I 0 INZ(56)

D bytesA 10I 0

D msgID 7

D reserverd 1

D data 40

D outXml s 256a

D itm s 20a

D fldName s 20a

/free

itm = getUrlVar('-value itm');

fldName = getUrlVar('-value fldName');

if itm = 'HAT';

outXml = %trim(fldName) + '|FOUND';

else;
outXml = %trim(fldName) + '|NOT_FOUND';

endif;

outXml = 'Content-type: text/plain' + x'1515' + outXml;

toBrowser(outXml : %len(%trim(outXml)) : gError);

*inlr = *on;

/end-free

//-----------------------------------------------------------------------

// @Author: Aaron Bartell

// @Modified:

//-----------------------------------------------------------------------

P getUrlVar B Export

D getUrlVar pi 32767A varying

D pCmdStr 300A value

D EOL C X'25'

D output S 32767A

D tgtBuf S 32767A

D outputFmt S 8A INZ('CGII0200')

D respLen S 9B 0

D tgtBufLen S 9B 0 INZ(%Size(TgtBuf))

D position S 5I 0

D error DS LikeDS(gError)
/Free

pCmdStr = %trim(pCmdStr) + X'00';

tgtBuf = '';

qzhbCgiParse(pCmdStr: outputFmt: tgtBuf: tgtBufLen: respLen: error);

if (%scan('-value':pCmdStr) > 0) And respLen > 0;

position = %scan(EOL: tgtBuf: 1);

if position < %size(tgtBuf) And position <> 0;

position += 1;

position = %scan(EOL: tgtBuf: position);

else;

position = 0;

endif;

if position = 0 And %scan(EOL: tgtBuf: 1) > 0;

respLen = respLen - 1;

endif;

endif;

if respLen > 0;

output = %subst(tgtBuf: 1: respLen);

else;

output = '';

endif;

return output;

/end-free

P E

You might also like