02
Sat, Nov
2 New Articles

Interfaces in ILE RPG IV--Finding the Middle Ground

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times
In my past two articles ("What? RPG IV a Better C Than C?" and "How Could Life Be Better?"), you have seen the basics of binding with C and the problems that can be averted by using C I/O functions instead of relying on the more static behavior of RPG I/O opcodes. In this article, you are going to see how you can enforce interface requirements for applications in the same functional group (written in RPG IV) by using binary linkage dependencies (function callbacks) imposed by modules that reside in the application binding directory, similar to techniques used in Java and C++ applications. Sample code demonstrating this strategy is included within this article. For the complete code, click here.

This strategy is, of course, completely voluntary. Should you choose to use it, you must adhere to programming standards and guidelines for maximizing reusability and ensuring consistent programming interfaces.

Binding Contracts

Object-oriented (OO) languages like Java and C++ enforce consistent interfaces by creating a hierarchy of abstract classes and interfaces that must be implemented in order to properly create an object of the desired type with the desired functionality. Since RPG IV is not an OO language, you must find creative ways to enforce an API and thereby ensure that functioning, concrete methods will be available (in your program) for callback processing (similar to callback processing requirements of the SAX API for parsing XML documents, as described in "Fast XML with RPG IV and SAX") from standard reusable services that exist in your API. This is referred to as establishing and enforcing contracts between a service requester and service provider (in this case, bound modules or service programs).

Contracts make reusability and interfaces work by requiring two-way or reciprocal binding commitments between a service requester and a service provider. They ensure that a generic utility or service provider can customize its behavior to meet client application requests by issuing callbacks to requester application functions. The requester application functions are enforced by dependent calls from the service provider (that must be resolved at bind time for successful compilation) to the service requester, to obtain application-specific information. This type of give-and-take exchange is best illustrated in Figure 1, which is discussed and shown later in this article.


Managing Consistency

An alternative (for providing a similar level of service) would be to pass an extensive list of arguments to the service provider when the initial callout request is first made. However, this adds the burden of passing those arguments down through successive calls to other functions that may not need them (or establishing undesirable global variables to retain the data) so that the information is available to the actual servicing functions when and if such services are needed. Besides creating added complexity to the process of maintaining and debugging the application, this strategy imposes no requirement to ensure that programs bind to all those services expected--and in some cases required--to deliver even a minimal set of program features and functionality. This could translate into inconsistent features or feature behavior in programs in the same application group, and consistency is a desirable objective in most development activities.

This is why abstract class and interface constructs have been developed and are so important in Java and C++ worlds and why COM/COM+ and JNI exist to manage interfaces in a mixed-language development environment--to enforce feature consistency and behavior and to improve reusability. "This is fine and dandy," you say, "but how do I translate that into practical terms that can be implemented into my daily RPG IV or C programming requirements?"


ILE--More Than IV %BIFs

First, you must convince yourself and others in your programming team that developing standard and consistent interface requirements is a task worth investing your time and future development on. If RPG IV or any other procedural languages assume a vital role in your future development activities, half the battle is won. The other half of the battle depends on your commitment to ILE beyond simple source conversion from RPG/400 to RPG IV. If your shop is content to exploit the benefits of new BIFs and the occasional new opcode but doesn't really care about developing consistent APIs that ensure reusability (beyond source), isn't interested in improving productivity (by reusing tested and proven components), and doesn't anticipate the need for mixed-language development, then this strategy is probably not for you.

However, if you are seeking a strategy that makes the most effective use of ILE and want to develop applications that move toward the style and structure of languages such as Java and C++, you will want to establish a practical approach for when and how to use the techniques you will learn from this article. For example, attempting to implement this strategy into legacy code that is barely RPG III much less ILE RPG IV is probably not a good idea. But new development projects that have a "standalone" quality about them are ideal for starting to work with this type of strategy. While conditions do not have to be ideal for using the techniques learned in this article, it is important to recognize that some projects are not suitable for interface implementations--such as applications that are locked to a vendor-controlled code base or applications that are so tightly integrated that the benefits of interface implementations cannot be fully appreciated without rewriting the entire application. Just as with integrating any new technology, you must size the projects. Weigh the expected turnaround and cost vs. benefits expected by users. Making the assumption that you have such a project, for purposes of this article, what techniques would bring you closer to interface enforcement?


Let's Shake on That!

Interface requirements, in the current state of ILE RPG IV applications development, can be ensured by establishing, maintaining, and--through programming standards and guidelines--enforcing reciprocal (handshake) binary contracts using well-thought-out and well-constructed sequence diagrams and binding directories that follow consistent application binding strategies. In Figure 1, you see a simple scenario in which such a concept is put to work.

http://www.mcpressonline.com/articles/images/2002/Interfaces%20in%20ILE%20RPGIV%20-%20Finding%20the%20middle%20groundV500.png

Figure 1: Here's an example of interfaces enforced by binding resolutions.

In the example in Figure 1, you have a requirement to print letters for W2 recipients (employees) and 1099 recipients (vendors), reminding them that their taxable compensation has exceeded provisions for withholdings so that they can make quarterly estimated payments (or suffer additional interest and penalties). In the example for this article, you will only be implementing the IReminder interface for 1099 reminder letters because it is simple to follow and makes the point.

As you can see from Figure 1, a service program (*SRVPGM) called Reminders resides in the Letters application group binding directory. It provides the reminder letter printing function, print_reminder(), by consecutively calling three other functions in that module, print_opening(), print_body(), and print_closing(). Each of these functions, in turn, issues a callback to the requester (in this case TEST1099) to the get_addressee(), get_taxable(), and get_approval() functions, respectively, to extract the data necessary to fill in the required portions of the standard letter format for reminders. This format is shown later in this article as compile-time table elements in the Figure 4 section [K] source.

Establishing interface requirements in this manner not only creates a handshake requirement between the requester of a service and the rendering service provider, but it also enables information (data) to reside in the appropriate modules that should know about and handle such information. This, in turn, allows service modules that render a generic or common service to remain relatively pure in purpose and functionality, which bodes well for stability and reliability of the service. Figures 2, 3, and 4 show this concept in more concrete terms with a source code example.

      *==================================================================
      *               I
      * MCPressOnline I  IReminder - Reminder Interface
      *               I
      *==================================================================
      *
      *-------- Reminder types
      *
     d RT_W2           c                   const('W2')
[A]  d RT_1099         c                   const('1099')

      *
      *-------- Reminder Interface definition
      *
      /if defined(IMPLEMENTS_IREMINDER)
[B]  d IReminder       ds
     d   Reminder_Type...
     d                               10a
     d   ptr_to_get_addressee...
     d                                 *   procPtr
     d                                     inz(%paddr('get_addressee'))
     d   ptr_to_get_taxable...
     d                                 *   procPtr
     d                                     inz(%paddr('get_taxable'))
     d   ptr_to_get_approval...
     d                                 *   procPtr
     d                                     inz(%paddr('get_approval'))

      *
      *-------- Prototypes to get current letter values
      *
     d get_addressee   pr              *   extproc('get_addressee')

     d get_taxable     pr             8f   extproc('get_taxable')

     d get_approval    pr              *   extproc('get_approval')

      /else
[C]  d IReminder       ds
     d   Reminder_Type...
     d                               10a
     d   ptr_to_get_addressee...
     d                                 *   procPtr
     d   ptr_to_get_taxable...
     d                                 *   procPtr
     d   ptr_to_get_approval...
     d                                 *   procPtr

      *
      *-------- Prototypes to get current letter values
      *
     d get_addressee   pr              *   extproc(ptr_to_get_addressee)

     d get_taxable     pr             8f   extproc(ptr_to_get_taxable)

     d get_approval    pr              *   extproc(ptr_to_get_approval)
      /endif
      *
      *-------- Print reminders prototype
      *
[D]  d print_reminder...
     d                 pr              n   extproc('print_reminder')
     d  reminder                           like(IReminder)

Figure 2: This is the IReminder interface in the abstract.

Step by Step

Figure 1 shows a sequence diagram depicting the messages (function calls) between the requester and service provider for reminder letters. Figure 2 shows the source code representation of the IReminder interface depicted in Figure 1. Figure 2 section [A] defines two constants that represent the supported reminder letter types (W2 and 1099). Of course, in this example, you will only implement the 1099 reminder letter type. [B] and [C] are mutually exclusive versions of the IReminder data structure, as depicted by the /if-defined(IMPLEMENTS_IREMINDER)/else-/endif. The [B] version is copied during compilation into those program source members that implement the IReminder interface, comprised of 2: get_addressee, 3: get_taxable, and 4: get_approval functions depicted in Figure 1. The [C] version is copied during compilation into the service provider that requires the callback services rendered by a properly implemented IReminder requester, to comprehensively service the 1: print_reminder function request issued by the requester.

As you can see, [B] in Figure 2 sets up a requirement for any requester implementing the IReminder interface by defining the initialization of three procedure pointers with the addresses for each of the three callback functions defined and depicted in the IReminder interface in Figure 1. This requirement ensures that the three functions (required by the service provider) will be implemented by the requester; otherwise, the program will fail to be created during the binding step and will result in messages like these:

      Definition not found for symbol 'get_taxable'.       
      Definition not found for symbol 'get_approval'.      
      Definition not found for symbol 'get_addressee'.     


Figure 2 section [C] ensures the service provider uses the same IReminder interface definition as the requester providing the callback services by residing in the same /Copy member, but it omits the initialization of the procedure pointers since it will be the recipient of the requester's initialized version on the callout to print_reminder(), which takes an IReminder type as its only argument, as shown in [D] of Figure 2 prototyping the print_reminder function. Whereas [B] and [C] of Figure 2 establish a consistent definition of the IReminder interface, [D] provides the callout that initiates the handshake requirement between the requester and service provider. While failing to include a call to print_reminder() in TEST1099 shown in Figure 3 [J] and [K] will not cause TEST1099 to fail the compile and binding steps, failure to include the call will prevent successful results from being achieved--something that will not go unnoticed. The IReminder /copy establishes the ground rules for subsequent communication between the requester and service provider of reminder letter services.

      *==================================================================
      *               I
      * MCPressOnline I  TEST1099 - Test 1099 Reminders Requester
      *               I             (IReminder Implementor)
      *               I
      *==================================================================
[A]  h dftactgrp(*no) actgrp(*new) bnddir('QC2LE':'LETTERS')

[B]   /define IMPLEMENTS_IREMINDER
      /copy qrpglesrc,IReminder

[C]  d address         ds
     d   name                        25a   inz('No Name')
     d   add1                        30a   inz('No adddress 1')
     d   add2                        30a   inz('No adddress 2')
     d   city                        15a   inz('No city')
     d   state                        2a   inz('NA')
     d   zip                         10u 0
     d   dob                           d
     d   balance                      8f

     d printf          pr                  extproc('printf')
     d                                 *   value options(*string)
     d                                 *   value options(*string:*nopass)
     d                                 *   value options(*string:*nopass)
[D]
     d stream_printf...
     d                 pr                  extproc('sprintf')
     d                                 *   value options(*string)
     d                                 *   value options(*string)
     d                                 *   value options(*string:*nopass)
     d                                 *   value options(*string:*nopass)
     d                                 *   value options(*string:*nopass)
     d                               10u 0 value options(*nopass)





      *
      *----- Miscellaneous constants and standalone variables
      *
     d newline         c                   x'25'
     d tab             c                   x'05'
[E]  d manager         s             25a
     d buffer          s            200a

      *
      *----- Applied versions of ADT address
      *
     d var             ds                  likeds(address) based(pVar)
[F]  d var1            ds                  likeds(address)
     d var2            ds                  likeds(address)

      /free
       monitor;

[H]    manager = 'Joe PayMeNow' + x'00';

       var1.name = 'John Doe';
       var1.dob = %date('03/23/1977':*usa);
       var1.add1 = 'Apt. 225';
       var1.add2 = 'c/o Jane Doe';
[I]    var1.city = 'Somewhere';
       var1.state= 'TX';
       var1.zip = 73533;
       var1.balance = 8180.16;

       pVar = %addr(var1);
[J]    Reminder_Type = RT_1099;
       print_reminder(IReminder);


       var2.name = 'Jane Doe';
       var2.dob = %date('12/08/1977':*usa);
       var2.add1 = '23 N. Lancaster';
       var2.city = 'Somewhere Else';
       var2.state= 'TX';
[K]    var2.zip = 77381;
       var2.balance = 22515.84;


       pVar = %addr(var2);
       Reminder_Type = RT_1099;
       print_reminder(IReminder);

       on-error *all;   // This is like MONMSG CPF0000 with branch to
                        // end
           printf('%s' + newline :'Fatal error occurred. +
                                   Processing haulted!');
       endmon;

       *inlr = *on;
      /end-free

      *---------------------------------------------------------------------
     p get_addressee...
     p                 b
      *---------------------------------------------------------------------
     d                 pi              *

      /free
       stream_printf(%addr(buffer)
                    :'%s' + newline +
                     '%s' + newline +
                     '%s' + tab + '%d' + newline
[L]                 :var.name
                    :var.add1
                    :%trim(var.city) + ', ' + var.state
                    :var.zip);
           return %addr(buffer);
      /end-free

      *---------------------------------------------------------------------
     p get_addressee...
     p                 e
      *---------------------------------------------------------------------

      *---------------------------------------------------------------------
     p get_taxable...
     p                 b
      *---------------------------------------------------------------------
     d                 pi             8f

      /free
[M]        return var.balance;
      /end-free
      *---------------------------------------------------------------------
     p get_taxable...
     p                 e
      *---------------------------------------------------------------------

      *---------------------------------------------------------------------
     p get_approval...
     p                 b
      *---------------------------------------------------------------------
     d                 pi              *

      /free
[N]        return %addr(manager);
      /end-free

      *---------------------------------------------------------------------
     p get_approval...
     p                 e
      *---------------------------------------------------------------------

Figure 3: TEST1099 is the IReminder implemented by the requester.

You can begin to see the IReminder interface in action by reviewing the source code of the TEST1099 program in Figure 3. Figure 3 section [A] establishes the binding requirements of the requester by having it run in its own unique activation group--as indicated by actgrp(*new)--and establishes QC2LE and LETTERS as the binding directories to search when resolving imports and function calls. Recall from previous articles in this series that actgrp(*new) is required to properly view output from the C printf function, which resides in a service program (QC2IO) in the QC2LE binding directory. The LETTERS binding directory will contain an entry to the Reminders service program that contains the print_reminder functionality you will see later in this article. Figure 3 section [B] shows how a requester states its intention to implement the IReminder interface by defining the IMPLEMENTS_IREMINDER external compiler variable just prior to the /copy definition of IReminder. This triggers the [B] version of IReminder in Figure 2 and ensures that it is copied into the requester source stream for compilation and binding.

Now, jumping right into the code, you will see that [C] of Figure 3 was the same Abstract Data Type (ADT), address, that was first introduced in the article, "What? RPG IV a Better C Than C?" as the basis of var, var1, and var2 variables shown in [F] of Figure 3. In [I] of Figure 3, var1 subfields are initialized with the values for the first 1099 reminder recipient, John Doe. Added to the ADT definition of the previous article is the balance field that you will use for taxable amount where required. Immediately below that code, in [J] of Figure 3, the address of var1 is used for the basing pointer (pVar) for var. Next, the Reminder_Type field, in the IReminder data structure, is set to the value of the constant field RT_1099. Finally, the print_reminder function is called with the single argument IReminder, which has already been initialized with the three pointers to each of the required functions (get_addressee, get_taxable, get_approval) used by the print_reminder function. Putting aside what print_reminder does, for a moment, you will see that [K] of Figure 3 does exactly the same for var2 as [I] and [J] did for var1, except this time for recipient Jane Doe.

Now, turn your attention to Figure 4 for an explanation of what print_reminder does and how it uses the three procedure pointers passed in Figure 3 sections [J] and [K] to access the needed functionality in TEST1099 to customize its behavior and accomplish its task--printing the 1099 reminder letter.

      *==================================================================
      *               I
      * MCPressOnline I  REMINDERS - Reminder Letters Service Provider
      *               I
      *==================================================================
[A]  h nomain bnddir('QC2LE')

[B]   /undefine IMPLEMENTS_IREMINDER
      /copy qrpglesrc,IReminder

     d lettxt          s             73a   dim(9) perrcd(1) ctdata
[C]  d buffer          s             73a
     d newline         c                   x'25'
     d tab             c                   x'05'

      *------------------------------------------------------------------
      *   Domestic prototypes for unexportables.
      *------------------------------------------------------------------
     d print_opening...
     d                 pr              n   extproc('print_opening')

[D]  d print_body...
     d                 pr              n   extproc('print_body')

     d print_closing...
     d                 pr              n   extproc('print_closing')

      *------------------------------------------------------------------
      *   Imported prototypes.
      *------------------------------------------------------------------
     d printf          pr                  extproc('printf')
     d                                 *   value options(*string)
     d                                 *   value options(*string:*nopass)
     d                                8f   value options(*nopass)

      *---------------------------------------------------------------------
     p print_reminder...
     p                 b                   export
      *---------------------------------------------------------------------
     d                 pi              n
     d reminderI                           like(IReminder)

      /free
           IReminder = reminderI;
           if (ptr_to_get_addressee = *null or
               ptr_to_get_taxable = *null or
[E]            ptr_to_get_approval = *null);
                 printf('%s' + newline :'Fatal error occurred. +
                        Processing haulted!');
                 return *off;
           endif;

           print_opening();
[F]        print_body();
           print_closing();
           return *on;
      /end-free

      *---------------------------------------------------------------------
     p print_reminder...
     p                 e
      *---------------------------------------------------------------------

      *---------------------------------------------------------------------
     p print_opening...
     p                 b
      *---------------------------------------------------------------------
     d                 pi              n

     d ptr_to_addressee...
     d                 s               *
     d today           s               d   datfmt(*usa) inz(*sys)

      /free
           // Print letter separator...
           buffer = lettxt(1);
           printf('%s' + newline: buffer);

           // Print reminder type phrase...
           if (Reminder_type = RT_W2);
[G]           buffer = tab + tab + 'Excess W2 Earnings Reminder';
           else;
              buffer = tab + tab + tab + '1099 Earnings Reminder';
           endif;
           printf('%s' + newline : buffer);

           // Print today's date...
           buffer = tab + tab + tab + tab + %char(today);
           printf('%s' + newline : buffer);

           // Print addressee information...
           ptr_to_addressee = get_addressee();
[H]        printf('%s': ptr_to_addressee);
           buffer = lettxt(8);
           printf('%s' + newline: buffer);

           return *on;
      /end-free
      *---------------------------------------------------------------------
     p print_opening...
     p                 e
      *---------------------------------------------------------------------

      *---------------------------------------------------------------------
     p print_body...
     p                 b
      *---------------------------------------------------------------------
     d                 pi              n

     d taxable_amount...
     d                 s              8f
     d i               s              5u 0

      /free
           // Get taxable amount from requester...
[I]        taxable_amount = get_taxable();

           // Print taxable amount line...
           buffer = %trim(lettxt(2)) + x'00';
           printf('%s %.2f' + newline :buffer :taxable_amount);
           for i = 3 to 8;
               buffer = lettxt(i);
               printf('%s' + newline: buffer);
           endfor;

           return *on;
      /end-free
      *---------------------------------------------------------------------
     p print_body...
     p                 e
      *---------------------------------------------------------------------

      *---------------------------------------------------------------------
     p print_closing...
     p                 b
      *---------------------------------------------------------------------
     d                 pi              n

     d ptr_to_approver...
     d                 s               *

      /free
           buffer = lettxt(9);
           printf('%s' + newline :buffer);
[J]        ptr_to_approver = get_approval();
           printf('%s' + newline: ptr_to_approver);

           return *on;
      /end-free
      *---------------------------------------------------------------------
     p print_closing...
     p                 e
      *---------------------------------------------------------------------
     ** CTDATA lettxt
     ***********************************************************************
     This is just a reminder that your taxable 1099 earnings of $
     have exceeded the federal filing requirements of $ 7450.00.  You should
[K]  review your current withholdings to determine if you should file
     quarterly payments to avoid interest and penalties for late filing.
     If your records do not agree with this amount, please contact our
     Accounts Payable dept to get a satisfactory resolution to this problem.

     Sincerely,

Figure 4: Print reminders service provider (Reminders)

The first major point to note in Figure 4, [B] is that the Reminders source module calls for the [C] version of IReminder in Figure 2 to be copied into the compile source stream by undefining the IMPLEMENTS_IREMINDER external compiler variable. This ensures that when the /copy (also shown in [B] of Figure 4) is encountered, it does not import the version of IReminder that would attempt to initialize the three procedure pointers and fail the compile. Since Figure 4 will be using the C printf function to print reminder letters to the display, Figure 4 section [A] shows the required binding directory, QC2LE, in the H-specification. Figure 4 [C] presents the lettxt table definition that contains the letter body, the buffer used for sprintf output, and the newline and tab definitions used in both of the printf and sprintf functions to format output.


Printing the Reminder

Now, jump to the print_reminder function implementation in Figure 4 section [E]. You will see first a test that ensures that only properly initialized procedure pointers have been passed in the reminderI argument of that procedure (just above [E]) that uses the same format of the IReminder data structure defined in Figure 2. If the test fails, a message will be printed to the display (using the C printf function) indicating a fatal error has occurred and control will return to TEST1099. Otherwise, control drops through the condition to execute the three subprocedures--print_opening, print_body, and print_closing in [F] of Figure 4--that constitute the print_reminder functionality. The prototypes for these functions are shown in [D] of Figure 4.


Just for Openers

The call to print_opening in [F] of Figure 4 passes control to [G] of Figure 4 to print the opening content for the reminder letters. The opening consists of a separator (asterisks), the reminder type title, the current date, and the addressee information. The addressee information is the focus of this discussion, because it is taken by issuing a callback to the get_addressee function in Figure 3 [L]. As shown in that function, the stream_printf, prototyped at Figure 3 [D], uses the C I/O function sprintf to print its results to a buffer or array of characters rather than to the screen as the printf function does. This allows the requester to determine the format it wishes the address to appear in rather than leaving that decision to the reminder letters service, thus allowing customization by the requester. In Figure 4, [H] the get_addressee function is called and returns a pointer to the buffer that contains the formatted address information for the current vendor assigned to the pVar pointer in [J[ and [K] of Figure 3.

How are you able to call functions that exist in TEST1099? When the initial call to print_reminder was made from one of the two codepoints in Figure 3 [J] and [K], the procedure pointers that were initialized (with the addresses of the three get functions) when TEST1099 was invoked were stored in the IReminder data structure related variables in [B] of Figure 2. With the first line of code executed in [E] of Figure 4, the Reminders' copy of IReminder (global data structure) is initialized with the values of those pointers. These pointers, in turn, are used in the /copy version of IReminder for Reminders to initialize the prototype function (pointer) identified by procedure pointer--rather than resolved external name--in Figure 2 [C] for each of the three required functions: get_addressee, get_taxable, and get_approval.


Just Fill In the Blanks

Next, the print_body function is called in Figure 4 [F] to print the body of the reminder letter from the table records in Figure 4 [K]. The second table record (after the asterisks separator line) requires the taxable amount to be "filled in" with the recipient's balance amount. This is accomplished by issuing a callback (shown in Figure 4 [ I ]) to the get_taxable function (shown in Figure 3 [M]) that simply returns the balance from the var data structure variable--whose basing pointer is set just prior to the call to the print_reminder function--to the appropriate recipient information variable (var1 or var2) address. Lastly, a For loop is entered to print the remainder of the reminder letter body lines in the lettxt table shown in Figure 4 [K].


Sincerely Yours

Finally, the print_closing function is called from Figure 4 [F] to print the closing of the standard reminder letter. In the example, this consists of two elements: the signoff word "Sincerely" followed by the name and signature of the department manager approving the reminder letter distribution. In this case, the manager was defined in Figure 3 [E] and initialized in Figure 3 [H]. Reviewing the source code in Figure 4 [J], you will see the callback issued to get_approval, defined in Figure 3 [N], which returns a pointer to the manager variable containing the name of the approving manager that is printed as the last line of the reminder letter. Control is then returned to the body of the print_reminder function, which then returns control to the requester (in this case, TEST1099).

The results from the example implementation in the IReminder interface by TEST1099 can be obtained by issuing a call to TEST1099 like the following:

CALL TEST1099

Your results should look exactly like that shown in Figure 5. To obtain these results, be sure to follow the build instructions included in the program banner of each source member.

Figure 5: This screen shows a sample result of running TEST1099.

What's Next?

The example given here was contrived to present a simple set of steps for defining and implementing interfaces in RPG IV. In practice, interfaces are usually much more extensive than the four functions depicted by Figure 1. However, this example does show you the basic principles and concerns for implementing interfaces. The companion downloadable source code for this article consists of not only the source presented, but also fixed-format versions (as denoted by an "@" attached to the end of the member name) of TEST1099 and Reminders (for those encumbered by an OS/400 version lower than V5) and the same functionality presented here as it would be coded in Java (Review1099). Comparing the Java and RPG IV free-form versions will give you a better appreciation and understanding of interfaces in an object-oriented language. But the story is not yet complete. If you want to see the ending, stay tuned to MC Mag Online for the last article in this series, "ILE RPG IV vs. C--The Final Conflict."


Jim Barnes is a freelance writer and independent consultant working in Houston, Texas. Jim can be reached at This email address is being protected from spambots. You need JavaScript enabled to view it..


Jim D. Barnes is a freelance writer and Systems Engineer in Plano, Texas.

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: