18
Thu, Apr
5 New Articles

Execute Bound Procedures Dynamically

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

If you're looking for information about using APIs to execute bound procedures, you may as well stop reading right now. This is about transparently executing alternate versions of bound procedures.

I recently needed to figure out how to do three things with ILE RPG:

  1. Configure an application with 100 or more parameters whose values vary by U.S. state
  2. Write a standard output document containing nonstandard sections that can be wildly different for each U.S. state
  3. Make such an application easy to maintain and understand

Configuring an Application with 100 or More Parameters

The first requirement isn't much of a challenge. It seemed obvious from the start that a configuration parameter file was the appropriate solution here. I designed a file that could hold default values and overrides by state as needed.

Configuration values are named in a way that would seem familiar to anyone who's ever worked with a UNIX resource file or the Windows registry. Each configuration value corresponds to a similarly named global variable in the application. A service routine loads and maintains the values. The application just uses them as if they are dust bunnies that appear effortlessly under the bed.

Writing a Standard Document Containing Nonstandard Sections

The second requirement is a little tougher. Configuration switches and option values can do quite a lot, but there are practical limits. A number of the requirements just couldn't always be satisfied with a standard routine. Historically, this problem is often resolved in the AS400/iSeries/i5/whatever world by using case selects, possibly in conjunction with subroutines or procedure calls based on the value of some internal variable, such as a state identifier. This particular application was already way past the level of complexity that can be intuitively understood by any human that I know. I didn't think I could afford the additional complexity of case selects and conditional subroutine execution. So how does one get from here to there without invoking a wormhole to Alpha Centauri? That's the subject of this article, but let's put it aside for a minute and talk about requirement three.

Making It Easy to Understand

You might be surprised at the amount of requirement detail you can absorb and intuitively use while writing a program. I think it's described as f(x) = Lim(Σф)x/Ю, as the number of telephone calls (Ю) approaches infinity. Anyway, there is a limit, looming like the door to tomorrow. No information returns from beyond it. There are probably sea monsters. I needed to get past it.

I decided to group related operations together in a way that would allow me to think of them as pseudo-operations, or black boxes. Instead of fetching the row from the database, setting the base color to red if the border matrix is less than .0379, and attaching the pedestal offset, I would simply GetFramis(). No other code in the program needs to know what's in a framis. In fact, the whole concept breaks down if any other routine is allowed to mess with the internal composition of the framis.

Once I've finished the first level of conceptual groupings, I can repeat the exercise and execute GetFramis(), AssembleThrom(), and PackageAssembly() just by calling the BuildProduct() function. This is minimal encapsulation and data hiding. The concept has been used successfully for years in some circles. As a strategy, it has its own problems. For instance, BuildProduct(), while possibly a useful grouping of functions, may not truly be a concept. If that's the case, using it increases the complexity of the program, rather than decreasing it. Instead of understanding BuildProduct(), the developer needs to understand that BuildProduct() really is the container for the GetFramis(), AssembleThrom(), and PackageAssembly() functions. That's not helpful. It would be a bad thing.

Having accomplished real consolidation of function into coherent, conceptually knowable black boxes, the total program now becomes knowable at different levels of granularity and scope. It's still unknowable overall, but the gross function is understandable by examining the execution of the highest level of black box concepts (functions), and succeedingly greater levels of detail are knowable as long as the scope of that knowledge is limited to the function containing the detail.

I hope I didn't short-circuit on that last paragraph.

Back to the Real Problem

I now have a situation where the following code might be executed:

If Nozzle.UpperBound > NOZZLE_LIMIT;
NewNozzle = GetNozzle(Nozzle.Spread);
EndIf;

That's fine most of the time, but some states place arbitrary restrictions on nozzle bandwidth, some require redundancy, some prohibit redundancy but require a disclaimer horn if bandwidth is less than 10 microns, and some prohibit distribution to felons but require notification of transshipment. A single GetNozzle() function that will handle all requirements for all states just isn't practical.

In the configuration table, I added a parameter called Get_Nozzle. I made the default value StdGetNozzle. If required, the value can be overridden for a specific state so that state AK might specify AkGetNozzle, state CA could specify CaGetNozzle, etc.

The startup utility functions, which implement the configuration for a state, load a variable pGetNozzle with the configured value so that when State = AK, pGetNozzle contains the value AkGetNozzle.

The startup module contains the following prototypes:

D StdGetNozzle    PR                  Like(T_NOZZLE)
D  aSpread                            Like(T_SPREAD) Const
D AkGetNozzle     PR                  Like(T_NOZZLE)
D  aSpread                            Like(T_SPREAD) Const
D CaGetNozzle     PR                  Like(T_NOZZLE)
D  aSpread                            Like(T_SPREAD) Const

It also contains the following variable definition:

D @GetNozzle      S               *   Procptr Export

The Export keyword means that this variable will be available to any module in the program that chooses to import it. The @GetNozzle pointer is set with a call to the GetFnPtr function:

@GetNozzle = GetFnPtr(pGetNozzle);

The GetFnPtr function returns the pointer:

     P GetFnPtr        B
     D GetFnPtr        PI            16*   PROCPTR
     D  ProcName                           Like(T_PROCNAME) Const
     D RtnVal          S             16*   PROCPTR
      /FREE
       If ProcName <> '*NULL';
         Select;
           When ProcName = 'StdGetNozzle';
             RtnVal = %Paddr(StdGetNozzle);
           When ProcName = 'AkGetNozzle';
             RtnVal = %Paddr(AkGetNozzle);
           When ProcName = 'CaGetNozzle';
             RtnVal = %Paddr(CaGetNozzle);

           // Unrecognized function name
           Other;
             RtnVal = *NULL;
         EndSl;
       Else;
         RtnVal = *NULL;
       EndIf;
       Return RtnVal;
      /END-FREE
     P GetFnPtr        E   

This could be done more elegantly with APIs, but since all of the optional modules are known to the service routine at compile time, I wanted to make it simple and obvious to anyone reading the code.

Once the procedure pointer value has been set, all the main program needs to do is declare the imported procedure pointer and the dynamic function prototype:

D @GetNozzle      S               *   Procptr Import
D GetNozzle       PR                  Like(T_NOZZLE)
D                                     ExtProc(@GetNozzle)
D  aSpread                            Like(T_SPREAD) Const

Now, the function named in the configuration variable (pGetNozzle) will be executed automatically by the mainline code when this line of code executes:

NewNozzle = GetNozzle(Nozzle.Spread); 

Since nozzles are manipulated only by the GetNozzle() function, there is no impact to the program as a whole. The AkGetNozzle() function is even free to call StdGetNozzle() and modify its result before returning to the main program. RPG doesn't support inheritance, but this is very close.

If there's a chance the @GetNozzle pointer might be null, it's a good idea to test for that before making the function call:

If @GetNozzle <> *NULL;
  NewNozzle = GetNozzle(Nozzle.Spread);
Else;
  // Do something to die gracefully
  // because we're not configured correctly.
EndIf;

It's not a bad trade-off for a lot of flexibility and virtually no additional complexity. Now, the main program can happily ignore the whole nozzle problem and get some work done.

Pete Hall is a Development Analyst with Virtual Care Provider, Inc. He lives in Wisconsin. He can be reached by email at This email address is being protected from spambots. You need JavaScript enabled to view it..

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$0.00 Raised:
$

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: