Other enhancements include extended capabilities for sorting and searching arrays, a new "mailmerge" scan-and-replace function, the ability to use the database's long names, and relaxed prototyping requirements.
So did the Easter Bunny bring plenty of RPG goodies in his basket this year? You betcha. There's a lot to like in this new release, but perhaps more importantly, IBM has delivered on the promise of starting to open up the language to allow others to participate in its evolution. Back in the fall of 2009, IBM began dropping hints about an upcoming feature for RPG that they referred to as Open I/O. Subsequently, the name was changed to Open Access (OA) or, formally, Rational Open Access: RPG Edition. One might conclude from this name that perhaps IBM intends to offer the same capabilities for COBOL in the future, but so far there has been no statement to that effect.
So what exactly is OA? Simply put, it allows programmers to increase the range of devices directly supported by the RPG language. With this feature, you can now use conventional op-codes such as READ, WRITE, CHAIN, and EXFMT to interface with devices such as browsers, Web services, mobile phones, and more. While you can certainly write your own drivers to take advantage of this feature, it is clear that IBM is targeting this capability at third-party software vendors and open-source providers. They can now integrate their offerings into the RPG language in ways that were previously difficult if not impossible.
Describing the complete mechanics of OA is beyond the scope of this short review, but we will talk a little more about it and our hopes for how it will be used at the end of this piece.
Time to Look into the Basket
For now, though, let's look at some of the other goodies the bunny delivered.
For us, the big hitters are expanded capability for sorting and searching arrays, a new "mailmerge" scan-and-replace function, the ability to use the database's long names, and relaxed prototyping requirements. We'll begin with the sort and search enhancements.
With the advent of V5R2, it became possible to define data structure arrays (i.e., with a DIM keyword) at the DS level. But at the time, IBM did not provide any means by which such arrays could effectively be sorted. To do that, you had to use the qsort function. That shortcoming is removed in V7, and you can now sort on any subfield in the array. For example, given the following DS array, I can perform a sort on qtyInStock, totalSales, or any of the other fields in the DS.
D products1 DS Dim(999) Qualified
D productCode 5a
D description 40a Varying
D totalSales 9p 2
D qtyInStock 5p 0
In the first example, the DS array is sequenced on the totalSales values, and in the second, the description.
Another nice addition to the SortA repertoire is that you can now specify whether the sort is to be in ascending or descending sequence. Previously, this was determined by the ASCEND or DESCEND keyword on the array definition, and SortA used the defined sequence, which of course meant that without playing games (remapping the array via pointers, etc.), any given array could only ever be in ascending or descending order. Now, you can use the op-code extenders (A) and (D) to specify the sequence in which the array is sorted. For example:
SortA(A) products1(*).totalSales; // Sort ascending sequence
SortA(D) products1(*).description; // Sort descending sequence
Even nested arrays can be sorted, as shown in the example below. Just as in the earlier example, the actual array to be sorted is identified by an asterisk (*) in the subscript position. The field name that follows identifies the field within the array on which it is sorted. In this example, the inner array containing the monthly sales values is sorted into ascending sequence, and then the outer array (i.e., products) is sorted into product code sequence.
D products2 DS Dim(999) Qualified
D productCode 5a
D description 40a Varying
D salesByMonth LikeDS(salesByMonth)
D qtyInStock 5p 0
D salesByMonth DS
D monthNumber 3p 0
D sales 9p 2
D i S 5i 0
// Sort each entry in turn into sales value sequence
For i = 1 to %elem(products2);
// Once individual entries have been sorted into sales value
// sequence sort the whole array into product code sequence
New and Enhanced BIFs
Of course to be truly useful, any enhancement in sorting needs to be matched with corresponding advances in searching, and the RPG developers haven't let us down.
The %LOOKUP BIF has been enhanced to allow for searching at any level within DS arrays. For example, to search for the product code '12345' in the above products2 array, we could code this:
entry = %LookUp( '12345': products2(*).productCode);
As with SortA, the asterisk is used to indicate the array level at which to operate.
The new BIF %SCANRPL fills a gap in the RPG BIFs set by combining the functionality of %SCAN and %REPLACE to provide a simple mechanism for text replacement. Suppose you have a variable (baseText) that contains the characters "Dear &name, you are the winner of an all-expenses-paid vacation. In order to claim your prize, &name, you must..." The RPG code shown below will result in all occurrences of the string '&name' being replaced by the content of the variable suckersName. If the variable contained "Alexander," the resulting string would be "Dear Alexander, you are the winner of an all-expenses-paid vacation. In order to claim your prize, Alexander, you must..."
newText = %ScanRpl('&name': suckersName: baseText);
I think you'll agree that this is a lot easier than coding a For loop by hand and using %SCAN and %REPLACE for each occurrence of the target string!
%LEN now offers an additional option that may make your coding for variable-length fields a little simpler. You can specify *MAX as the second parameter to %LEN to retrieve the maximum number of characters that can be stored in the specified variable-length field. Without *MAX, the BIF will return the actual length of the field's content, as it does today. It's much simpler to determine the maximum capacity by coding %Len(myVaryingField: *Max) than to have to determine whether the field has a two- or four-byte length and then derive the capacity by subtracting that number from the length given by %Size(myVaryingField). Not to mention having to divide by 2 in the case of UCS-2 fields!
New Options for Subprocedures
There are two major changes in this arena. The first is a long-awaited relaxation in the compiler's rules regarding prototypes. Until now, any procedure that was going to be defined and/or called within a module had to have a prototype. That is no longer the case.
This is really good news for those of you who use internal subprocedures extensively. Now you need only code the procedure interfaces for your internal subprocedures. There's no need to code the matching prototypes; the compiler will be quite happy. That brings the amount of work involved in creating a new subprocedure closer to the level needed for a subroutine, thereby removing yet one more of the perceived barriers to adoption of this "new" technology. We put "new" in quotes because, despite having been around since V3R2, subprocedures are still not as widely used as they should be.
The second change is good news for those of you who would like to use large return values (e.g., a large record set) but have found the performance penalty to be too great. You can now code large return values with impunity as long as you include the new keyword RTNPARM on the subprocedure's PR and PI definitions.
When you use this option, under the covers, the return value is handled as an additional parameter. In fact, its pointer will be passed as the first parameter to the subprocedure. Of course, this means that while the change is largely transparent to your code, what was parameter 1 is now parameter 2, etc. So if you add this keyword to an existing subprocedure that uses optional parameters (i.e., Options(*NoPass)), you will have to adjust the values that you test for with %Parms. In fact, IBM recommends that you consider using the new BIF %PARMNUM when determining the number of parameters passed. %PARMNUM(parmName) will return the ordinal number in the parameter list of the parameter in question. So if you referenced the first parameter, it would return 2 if RTNPARM were in effect and 1 otherwise. By combining %Parms with this value, you have a bulletproof method of testing for optional parameters that will also survive most changes in the parameter list. For example:
// Check if optional parameter supplied
If %Parms >= %ParmNum(optionalParm1);
This will continue to work even if RTNPARM is added to the subprocedure definition. Had a simple test of %Parms been used, you would have to examine the code to determine which constants/literals needed to be changed when RTNPARM was added.
For many, many years (going all the way back to the System/38), the database has supported the use of longer alias names as an alternative to the cryptic 10-character names that we normally use. Indeed, many COBOL shops have taken advantage of them. Usage of alias names increased with the advent and growth in popularity of SQL, and more recently, the arrival of the MySQL DB2 Storage Engine added to the growth in usage. But during all this time, RPGers were locked out of using these longer names because the language was tied to the old I- and O-specs and their limited field names.
When result field I/O was first introduced for externally described files (back in the V5R2 timeframe), we commented that this might herald the arrival of alias names into RPG. Well, it has taken a few releases, but it's finally here. As of V7.1, you can specify the ALIAS keyword on the F-spec of any file for which no I- or O-specs will be generated—that is, any file that is specified as QUALIFIED or that is defined within a subprocedure. Not only will the alias names be used for the fields from such files, but they will also be used for any DS described with LIKEREC. Similarly, when defining an externally described DS, the ALIAS keyword can be specified, and the compiler will use the longer alias names rather than the normal short names. In cases where the alias name does not meet RPG naming standards, the compiler reverts to using the short name. This would happen, for example, when the alias name is in mixed case and in quotes (e.g., "longMixedCaseName"). And for those of you who think you can't do that…oh yes, you can!
Other Bits and Pieces
Before we talk about Open Access, we should mention a couple of other features. These are also important but are generally of interest to a smaller percentage of the RPG community than those we have discussed so far.
The first is the ability to encrypt the listing debug view. This allows you to distribute debuggable programs without the fear that the source can be compromised. This functionality is of interest mainly to ISVs, but we also know a number of customers who would benefit from it. As security standards are tightened, we expect this feature to become increasingly important even for those who for other reasons do not put debuggable code into a production environment. The feature is enabled by specifying the DBGENCKEY keyword on the compile command. The listing view will be encrypted using the specified key, and in a subsequent debug session, the source will be visible only if that key is entered.
The second is that RPG is now fully teraspace-capable—useful if you are working with large amounts of dynamically allocated storage or if you simply need amounts of automatic storage that exceed the standard limits. This capability is controlled by the new compile keyword STGMDL (which can also be specified in the H-spec). In addition to being able to define that the module uses the teraspace memory model, you can alternatively specify that it inherits the storage model of whoever calls it. As you might expect, the memory-management operations (%Alloc, %Realloc, etc.) have been enhanced to enable teraspace storage requests to be made. The type of storage that these routines work with is now controlled by the keyword ALLOC, and you can request the traditional storage model (*SNGLVL), the teraspace model (*TERASPACE), or whichever model has been inherited (*STGMDL).
There may be other features that we have missed, but this is the list as we see it today.
Last But Not Least: Open Access
There has been much speculation about this new feature since it was first hinted at by Ian Jarman at the RPG and DB2 Summit in October of 2009. But only now are the full details becoming available.
Before we talk about the technical aspects of how this all works, let's take a quick look at how you might specify the use of an OA handler that allows "Display File" processing to actually control a browser.
FDspFile CF E WORKSTN
Notice the new HANDLER keyword. It can identify a service program subprocedure, as it does in this example, or a program. Apart from the simple keyword, there is nothing specific that you have to do. The "heavy lifting" involved in mapping EXFMT/READ/WRITE etc. operations to the browser are the responsibility of the handler. From your perspective, nothing else changes. Of course, the writer of the driver may have introduced a few additional requirements (for example, that a new record format be used to supply information that would not have been required of the 5250), but all of the required actions will simply use conventional RPG I/O operations.
In some respects, OA has some similarities with the facilities provided by SPECIAL files, but it goes beyond what SPECIAL files offered. In particular, OA can be used with any RPG file type. SPECIAL files were limited to sequential files. In addition, SPECIAL files received minimal information about the file operation being requested. It was therefore difficult to write generic SPECIAL file routines. OA is a completely new architecture that will undoubtedly evolve over time. SPECIAL files were stuck in the past, and their architecture made it difficult to extend their capabilities.
Open Access in fact provides two interface methods. The first is the closest to that used by special files in that it simply passes data between the RPG program and the user-supplied handler as data structures. The second provides not only the actual data but also a full description of each field in the record, including its length, data type, and usage. This makes writing generic handlers much simpler. The handler sets the type of interface it wants to use at the time the file is opened.
What Can It Be Used For?
The sky is the limit really. Of course, the ones that will initially grab the headlines will be handlers that provide a browser interface to replace 5250 output. We have already been lucky enough to see a demonstration of this capability from one of the ISVs that has been working with IBM. To say that we were impressed is to put it mildly. They have gone far beyond what we expected to see at this early stage. But beyond the browser, there are many other possibilities. What about generating an Excel spreadsheet by simply WRITEing to a "Printer" file? Or accessing random elements within an XML document by using CHAIN? What about sending an email by "printing" to the appropriate handler? How about obtaining parcel-tracking information from a courier's Web service by using a CHAIN with the tracking number as the key to retrieve the tracking data? We could go on and on, but we think you get the point.
Beyond the Release Announcement
Hopefully, in the not-too-distant future, IBM will run some form of joint announcement with the ISVs participating in the program so that we can all get to see just what is in store for us. Of course, OA is a natural for open-source providers too, so perhaps folks like Scott Klement, Aaron Bartell, or Giovanni Perotti will step up with new offerings that exploit OA. Time will tell. And here's some really good news: OA will be available not just on V7.1, but also on V6.
Once the dust settles a little, look for articles on how to write your own handlers. There is such a lot you can do with them. Those of you who enjoy using modern programming techniques but just cannot convince the RPG IIIers in the shop to join in may find that this allows you to provide them with an interface that is so easy to use that they just can't refuse.
So Much to Be Excited About
Hopefully, we've given you enough here to get you excited by the features that V7 brings. For sure, it's going to be an interesting few months as the community wakes up to what can be done with the new OA feature. Many new and updated products will be appearing in the market, all of which can only be good for our favorite platform and language. We could use a little excitement!
One final point: Although at the time of writing we do not know the final position, we understand that it is Rational's intent to charge a fee for the additional runtime support that underlies Open Access. It has been suggested that the fee will be moderate, but we're going to have to wait and see if Rational's idea of "moderate" reconciles with ours.