RPG IV at V5R1 Big Changes in RPG Land! - Partner400 ... · RPG IV at V5R1 Big Changes in RPG Land!...

34
Your Partner in AS/400 and iSeries Education RPG IV at V5R1 Big Changes in RPG Land! Jon Paris jon.paris @ partner400.com www.Partner400.com OCEAN Technical Conference Catch the Wave If you thought that RPGIV had changed over the years well "You ain't seen nothin' yet!!" The latest release of the language brings: Tighter integration with Java Any number of new Built-In Functions A completely new and greatly improved method of error handling And to top it all - completely free-format RPG IV calc specs! The author, Jon Paris, is co-founder of Partner400, a firm specializing in customized education and mentoring services for AS/400 and iSeries developers. Jon's career in IT spans 30+ years including a 10 year period with IBM's Toronto Laboratory. Jon now devotes his time to educating developers on techniques and technologies to extend and modernize their applications and development environments. Together with his partner, Susan Gantner, Jon authors regular technical articles for the IBM publication, eServer Magazine, iSeries edition, and the companion electronic newsletter, iSeries EXTRA. You may view articles in current and past issues and/or subscribe to the free newsletter at: eservercomputing.com/iseries. Feel free to contact the authors at: Jon.Paris @ Partner400.com and Susan.Gantner @ Partner400.com (c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 1-2

Transcript of RPG IV at V5R1 Big Changes in RPG Land! - Partner400 ... · RPG IV at V5R1 Big Changes in RPG Land!...

Your Partner in AS/400 and iSeries Education

RPG IV at V5R1

Big Changes in RPG Land!

Jon Paris jon.paris @ partner400.comwww.Partner400.com

OCEAN Technical ConferenceCatch the Wave

If you thought that RPGIV had changed over the years well "You ain't seen nothin' yet!!"

The latest release of the language brings:Tighter integration with JavaAny number of new Built-In FunctionsA completely new and greatly improved method of error handlingAnd to top it all - completely free-format RPG IV calc specs!

The author, Jon Paris, is co-founder of Partner400, a firm specializing in customized education and mentoring services for AS/400 and iSeries developers. Jon's career in IT spans 30+ years including a 10 year period with IBM's Toronto Laboratory. Jon now devotes his time to educating developers on techniques and technologies to extend and modernize their applications and development environments.

Together with his partner, Susan Gantner, Jon authors regular technical articles for the IBM publication, eServer Magazine, iSeries edition, and the companion electronic newsletter, iSeries EXTRA. You may view articles in current and past issues and/or subscribe to the free newsletter at: eservercomputing.com/iseries.

Feel free to contact the authors at: Jon.Paris @ Partner400.com and Susan.Gantner @ Partner400.com

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 1-2

Agenda

New Built-Ins Error CheckingJava EnablementFree Form Calcs Run-Time File OpenOther Enhancements

In this unit, we will study the very significant features added to RPG IV in Version 5, Release 1. Some of these features will dramatically change the way RPG programs are written in the future.

We suggest you read the article entitled "New Language? No, It's RPG IV" by Hans Boldt and Barbara Morris in IBM Rochester's iSeries Magazine, the May 2001 issue. You can review the article on the web at:

http://www.the400resource.com.

Take the link for Previous Issues and select May 2001. Then link to the article. It provides insight into the importance of some of these new features from the perspective of the primary RPG IV compiler developers.

In case you are interested, you can also find articles written by Jon Paris and Susan Gantner at this same web site. In particular you might find those in the February, March and July 2001 issues of interest as they discuss programming for the web in RPG.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 3-4

Date Durations in Expressions!%DIFF( Date1 : Date2 : DurationType )

Calculates a duration between date, time or timestamp valuesThat is, it replaces that function of SUBDUR

Other duration calculations are performed by simple + and - operatorsIn conjunction with new duration Built-Ins

More on this in the next chart

Duration types are as per ADDDUR/SUBDURThat is *MS, *S, *MN, *H, *D, *M, *Y

And the long-winded versions *MSECONDS, *SECONDS, *MINUTES, etc.

* Is the loan due within the next 6 months ?C DueDate SUBDUR Today MonthsToRun:*MC IF MonthsToRun < 6

C IF %Diff(DueDate : Today : *M ) < 6New version

Hurray! Finally date duration support in expressions! This is a feature that RPG IV programmers have been wanting since V3R1. Most RPG IV programmers much prefer using the free-form type operation codes, but the powerful date duration operations still required the fixed format to use ADDDUR and SUBDUR.

The code example here illustrates the ability to replace the ADDDUR and SUBDUR operation codes we saw in the Dates unit earlier with expressions using either %DIFF or a simple arithmetic add (+) operation in combination with the duration BIF (%Years, in this example).

Most RPG IV programmers quickly became addicted to the free form operation codes (e.g., EVAL, IF, etc.) and were dismayed to discover that in order to use the powerful date duration support built in to RPG IV from the beginning, they were forced to revert to the Factor 1, Factor 2, etc. format.

On the next chart, we will show how to also replace the requirement for a MOVE operation code to get numeric data to a date or time field.

Now, all date data type operations can be done with free form operation codes. The significance of this will be even greater when we look at the new "completely" free-format C specs later in this unit.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 5-6

Other Duration FunctionsAdding a duration to a date, time or timestamp

This function can now be performed by simple additionThe duration is supplied via the appropriate BIF

e.g. %MINUTES, %HOURS, %DAYS, %MONTHS, %YEARS, etc.

Multiple durations can be handled in a single statement

Subtracting a duration is similari.e. It is achieved through the use of simple subtraction

* These original duration calculationsC ContractDate AddDur Cyears:*Y ExpiryDateC ExpiryDate AddDur CMonths:*M ExpiryDateC ExpiryDate AddDur 1:*D ExpiryDate

C ExpiryDate SubDur 90:*D WarningDate

* Can be replaced by these calculations using the new duration BIFs C Eval ExpiryDate = ContractDate +%Years(CYears)C +%Months(CMonths) +%Days(1)

C Eval WarningDate = ExpiryDate - %Days(90)

The new duration calculations on the previous chart were taken from the program below. It demonstrates the use of the duration BIFs and displays the results of the calculations.

D ContractDate S D Inz(D'2000-03-30') D ExpiryDate S D D WarningDate S D

D CMonths S 3P 0 Inz(6) D CYears S 3P 0 Inz(1)

C Eval ExpiryDate = ContractDate +%Years(CYears) C +%Months(CMonths) +%Days(1)

C ExpiryDate Dsply

C Eval WarningDate = ExpiryDate - %Days(90)

C WarningDate Dsply

C Eval *InLr = *On

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 7-8

%Date - Convert to Date%Date( { expression { : date format } } )

Converts a character or numeric field to a datei.e. It performs the same function as a MOVE to a date

Second parameter specifies the format of the source fieldAlso works with TimestampsIf first parameter is omitted then the system date is returned

D CharDate1 S 6A Inz('011549')D CharDate2 S 6A Inz('031954')

D ISODate S D DatFmt(*ISO)D USADate S D DatFmt(*USA)

* Loading a date field from a character field prior to V5R1C *MDY0 MOVE CharDate ISODateC *MDY0 MOVE CharDate USADate

* The equivalent code in V5R1C EVAL ISODate = %Date(CharDate1: *MDY0)C EVAL USADate = %Date(CharDate2: *MDY0)

The %DATE, %TIME and %TIMESTAMP BIFs are used to remove the requirement for a MOVE or MOVEL operation to convert data from character or numeric fields to date/time/timestamp fields. Much like %CHAR will format date data into a character field, %DATE will do the reverse for character fields. The second (format) parameter specifies the format of the data being converted (the data in the first parameter). If the format is not specified, the default format for the program is used. If you recall from the Dates and Times unit, the default format for the program is the format specified with DATFMT (or TIMFMT for time fields) on the H spec or, if not specified on an H spec, it will be *ISO.

Note that the format of the date (or time) returned will always be *ISO.

You may specify either *DATE or UDATE as the first parameter to get the job date into a date field. And you may omit the parameters altogether to retrieve the current system date (or time) - much like the TIME operation code.

Note the difference between job date and system date (*DATE or UDATE vs. %Date with no parameters). Also note that initializing a date field to *SYS using the D spec keyword only sets the INITIAL value for the field. So if a program runs over midnight, the initialized value in a date field defined with INZ(*SYS) will be different from that returned by %DATE without parameters.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 9-10

* Determine if payment is more than 90 days overdue - prior to V5R1

C *MDY0 MOVE DueDate WorkDate C TIME Today

C Today SubDur WorkDate Temp:*DC If Temp > 90C : ...... : ..............................C EndIf

* And the same calculation in V5R1C If %Diff(%Date(): %Date(DueDate: *MDY0) C : *Days) > 90C : ...... : ..............................C EndIf

Combining %Diff with %DateThe power of the new BIFs comes when combining them

In the example below it has significantly reduced the amount of codeIn fact the difference is even greater than shown since in the new version there is no need to define the work fields Temp, Today and WorkDate

Returns the system date

In this example, you can see how much more powerful it is to be able to use date operations in expressions. In this small example alone, we did away with 3 temporary work fields!

Note the use of %Date for 2 functions - first to retrieve the current date for the calculation and second to convert the DueDate field to a date form so it can be used in the duration calculation (via %DIFF).

We haven't talked about Free form calcs yet, but another reason to use the BIFs instead of the fixed format versions is to be able to write calcs in the new free format for V5R1.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 11-12

%SubDt - Extract a Part of a Date%SubDt provides a freeform equivalent to EXTRCT

It works with Dates, Times and Timestamps

The first parameter identifies the source fieldThe second identifies the portion to be extracted

Values are the same as for EXTRCT i.e. *MS, *S, *MN, *H, *D, *M and *Y

The benefit of the BIF shows in combined operationsSuch as the second and third example below

* Extract the day number from the field ProcessDateC Eval DayNumber = %SubDt(ProcessDate:*D)

* Combine %SubDt with %Date to obtain the day of the monthC Eval DayOfMonth = %SubDt(%Date():*D)

* Use to supply a subscript to access the month nameC Eval ThisMonth = MonthName(%SubDt(%Date():*M))

The second and third examples in this code sample combine the use of %DATE with no parameters to retrieve the current date and the %SUBDT to to extract a portion of the date value.

Imagine, for example, that you have an array with the names of the 12 months named MonthName. Then the third example here would find the appropriate month name from the array using the value of the month portion of the current date as an index value.

Be careful not to confuse this BIF with %DIFF. The name of it sounds unfortunately close to SUBDUR. In reality, its function is nearly identical to EXTRCT.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 13-14

%CHECK and %CHECKR%CHECK( Compare : String { : start-position } )

Works the same as the Op-code CHECKIt returns the position of the left-most character in String that is not one of the characters in Compare.

Companion function %CHECKR matches the Op-code CHECKRIt returns the position of the right-most character in String that is not one of the characters in Compare.

Search starts at specified start-position if suppliedOtherwise starts at beginning (%CHECK) or end (%CHECKR) of field

If all characters match, '0' is returned

* Check Input string contains only valid numeric characters C ValidNumeric CHECK InputString ErrorPos

C IF ErrorPos > 0: : :

* Can be replaced by thisC IF %Check(ValidNumeric : InputString) > 0: : :

The code example on this chart illustrates replacing the CHECK operation code with the %CHECK BIF. There are many advantages of using the BIF over the op code, including the shortened logic flow as illustrated in this example. Note that we not only reduced the lines of C specs needed, we also eliminated the need for the temporary field ErrorPos.

In the code example above, the field ValidNumeric would typically be a named constant defined something like the following:

D ValidNumeric C '1234567890'

Note that %CHECKR (check reverse) does the same as %CHECK, except the comparing is done beginning a the end of the string and moving left (right to left). This is one technique that can be used to find the position of the last non-blank character in a field (which might be thought of as the length of the content of a character field). The code to do this would look something like the following: C Eval Length = %CheckR(' ' : InputString)Of course, there are other ways to accomplish that task in RPG IV. See if you can think of another way (without using an array!)

Other ways to find the length of the content of a character field include using a combination of %Len and %TrimR, such as: C Eval Length = %Len(%TrimR(InputString))Or if InputString were defined as a variable length field (keyword Varying), then the %Len could be used without the need of %TrimR.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 15-16

One more nail in the *INnn Coffin!

%LOOKUPxx(SearchFor : Array { : StartIndex {: # Elems} } )Provides the same basic function as the LOOKUP Op-Code

Unlike LOOKUP, SearchFor and Array do not have to match in size and decimal positions Also provides optional number of elements to search in addition to starting index

%LOOKUP - Searches for an exact match%LOOKUPLE - Searches for an exact match, or the closest item lowerThere are also %LOOKUPLT, %LOOKUPGE, and %LOOKUPGT

I'll leave you to guess what they do!

Returns the Index number if SearchFor is foundOtherwise it returns Zero

The LOOKUP Op-code would have set the index (if supplied) to 1Also %LOOKUP will not change the value of the starting index

%LOOKUP and Friends

Note the differences in behavior between %LOOKUP and the corresponding operation code as shown on the bottom of this chart. Also note that the BIFs %FOUND and %EQUAL are NOT set as a result of using %LOOKUP, which is different for the operation code. Also note that %LOOKUP can NOT be used to look up values in tables (unlike the LOOKUP operation code).

However, those of you who use Tables will be pleased to know that there are corresponding table lookup versions of these BIFs. Not surprisingly they all start with %TLOOKUPxx.

The basic syntax for these BIFs is %TLOOKUPxx(searchfor : table {: alttable {: # elems} } )

%TLOOKUPxx sets the current table element for the specified table and also the alternate table if specified.

In case the meaning of "One more nail in the *INnn Coffin!" escapes you - LOOKUP was one of a very few operation codes as of V4R4 that still required the use of resulting indicators (the Hi/Lo/Eq type) and so it required that numbered indicators still be used. Now, programmers can (and should!) use the %LOOKUP BIF instead and remove some of those last remaining numbered indicators - replacing them instead with either named indicators (e.g., to communicate with Display Files) or BIFs, such as %EOF, %FOUND, %ERROR, etc.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 17-18

%ALLOC %CHECK %CHECKR %LOOKUPxx%OCCUR %REALLOC%SHTDN %SQRT%XLATE

Other "Op-code" Built-insWant a free-form alternative for all operations?

For example SHTDN sets an indicator (yuck!) that has to be testedThe new BIF %ShtDn can be used directly in a test

Others require a multi-step approach - for example:ALLOC (%ALLOC)REALLOC (%REALLOC)SQRT (%SQRT) XLATE (%XLATE)

C ShtDn 99 C If *In99 C If %ShtDn

C Eval BytesReqd = ArrSize * %Size(ArrElem)C Alloc BytesReqd pArray C Eval pArray = %Alloc(ArrSize * %Size(ArrElem))

These BIFs work in a similar fashion to the operation codes of the same name.

In every new RPG IV release, some operation codes are "replaced" with new BIFs. Fixed format operation codes are a dying breed! Of course for compatibility reasons the "old" opcodes are still supported but most people will prefer to use the new BIFs.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 19-20

Error CheckingMost RPG errors are handled by an error indicator

Or more recently by the (E) extender and %Error

This method only handles certain predefined status codesAnd then only for certain Opcodes

For example, it is not available on MOVEs

Most common alternative is a *PSSR or INFSR error routine Good general purpose cover but very difficult to recover

For example there is no way to return from the error routine to the next sequential instruction

Ta Dah!!Version 5 introduces a new option - MONITOR

Similar to MONMSG but even better

MONITOR should change the way you programAssume success and handle the exceptions

In a CLP, MONMSG can be used either for a specific CL command or for the entire CL program, depending on where you place the MONMSG statement. The RPG Monitor operation is applied to blocks of logic, so that you can monitor for an error on an entire block of code and react to the error in a single bit of logic.

Despite the fact that the free from calc specs get most of the attention for V5R1, the support for Monitor may well represent the most significant of the enhancements to the compiler in this release. Examples on the following charts illustrate how this powerful feature might be used.

Why should MONITOR change the way we program?

In RPG we tend to assume failure i.e. we always have to test to see if something worked. In some cases (such as handling dates) we test (TEST) to ensure that a subsequent operation (MOVE number to date field) will succeed. We do this despite the fact that probably 99.999% of the time the data is valid, but we do it anyway because handling the exception is too difficult.

In CL we tend to program for success. We issue the command (CPYF or whatever) assuming that it will work and then use MONMSG to catch the exceptions.

MONITOR allows us to apply this kind of philosophy to our RPG code - except it is even better and more flexible than MONMSG as you will soon see.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 21-22

C MONITOR C .... C code that might cause an error C .... C ON-ERROR status codes to handle C .... C code to handle these errors C .... C ON-ERROR status codes to handle C .... C code to handle these errors C .... C ENDMON

MONITOR begins the group

Code block to monitor

ON-ERROR begins the On-Error logic

Status Code Options are:*ALL Handle all status codes(Default)

*FILE Handle File status codes (01000 - 01999) *PROGRAM Handle Program status codes (00100 - 00999)

OR List of status codes separated by colons (:)

Error Checking with MONITOR Support

The Monitor Group consists of a Monitor statement, one or more ON-ERROR statement groups and an ENDMON statement. If an error occurs when the monitor block is processed, control is passed to the appropriate ON-ERROR group, based on the status code(s) listed on the ON-ERROR statement. If you do not specify any parameter on the ON-ERROR statement, it handles all types of errors - in a similar fashion to MONMSG with no message number specified. An ON-ERROR group continues until another ON-ERROR group or an ENDMON is encountered.

If all the logic in the Monitor Group (prior to the ON-ERROR groups) executes without error, control then passes to the first statement following the ENDMON.

A Monitor Group may be nested within another Monitor Group. When it is, the inner-most group gets control first on an error.

Branching operations are not allowed in a Monitor Group (the logic being monitored), but they are allowed - albeit not recommended - in an ON-ERROR group.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 23-24

MONITOR Group - Example 1

FFile1 IF E Disk

D StringError C 00100 D ExtractedData S 40A Varying

C READ FILE1

C DOU %EOF(File1) C MONITOR

C EVAL ExtractedData = %SUBST(InputData : 1 : C %SCAN('***': InputData) - 1) C 'Found ***' DSPLY

C ON-ERROR StringError

C EVAL ExtractedData = InputData C 'No *** Found'DSPLY

C ON-ERROR C 'Other Error' Dsply C ENDMON

C READ FILE1 C ENDDO

In this code example, File1 contains a single 40 character field named INPUTDATA. The INPUTDATA field contains a string of characters that is terminated with the special delimiter value "***". The purpose of this program is to read the input data and extract only the "valid" (defined as the data in the field prior to the delimiter characters. This is easily accomplished with the first EVAL statement, which scans for the delimiter characters and then extracts the substring of the field's data to the character preceding the delimiter location. (Note that both %SCAN and %SUBST were both covered earlier in the unit on Using Expressions.)

What would happen in that first EVAL statement if there were no delimiter value found in the INPUTDATA field - i.e., if the %SCAN returned a value of zero? A string error will occur - status code 00100 - because a value of zero is invalid as the length parameter to the %SUBST BIF.

So, we have coded for that possibility by monitoring the first EVAL operation for errors. If the string error specifically is encountered (status code 10100), the program will branch to the first ON-ERROR group and "fix" the problem by simply moving all the data in INPUTDATA to the extracted field. If any other type of error occurred, the second ON-ERROR group would get control.

Of course, in a real application, we would not use the DSPLY operation codes shown here. This simply provided us an easy way to verify that the code was performing as we expected.

Of course, this string error condition could be avoided by doing the %SCAN in a separate EVAL and only if the result were greater than zero, proceed with the %SUBST operation. However, with that alternative solution, one would need to either create a temporary field to hold the result of the %SCAN or else one would need to perform the %SCAN two times. If you are expecting the %Scanned for character to be present 99.9999% of the time, saving and testing this temporary value is a waste of time. Far quicker to allow the %Scan to fail and deal with the problem then.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 25-26

D DataIsClean S N D DecDataErr C 907

C DOU DataIsClean

C MONITOR C Eval Processing = 1 C Eval Num1 = InpNum1 C Eval Processing = 2 C Eval Num2 = InpNum2 C Eval Processing = 3 C Eval Num3 = InpNum3 * If we get this far all data is clean C EVAL DataIsClean = *On

C ON-ERROR DecDataErr * Error during numeric moves etc. Clean up data and try again C EXSR CleanUpData

C ENDMON C ENDDO

MONITOR Group - Example 2Here MONITOR is being used to help "clean up" Decimal Data errors

Using this method avoids having to test every record

"Missing" code from this example is shown in the notes

D DataIsClean S N D DecDataErr C 907

D Processing S 1S 0 C READ File1 * File1 record contains fields InpNum1, 2 and 3 C EVAL DataIsClean = *Off C DOU %Eof(File1)

>>> *** The main portion of the code in the example above goes here

C READ File1 C EVAL DataIsClean = *Off C ENDDO C EVAL *InLR = *On

C EndSr

C CleanupData BegSr * We use 9999 as a "default" value if an invalid value is found * We could also have added error reporting and/or more sophisticated logic to try to * "guess" what the invalid field should have been.

C Select C When Processing = 1 C Eval InpNum1 = 9999

C When Processing = 2 C Eval InpNum2 = 9999

C When Processing = 3 C Eval InpNum3 = 9999

C EndSl

C EndSr

Note: You will find additional notes that describe the operation of this code at the end of the handout.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 27-28

Mixing Java and RPGRPGIV subprocedures can be used as Java Native Methods

RPGIV programs are also able to invoke Java methods

To support this many new features have been addedExtensions to prototyping support

To identify the Java methods to be used and their parametersA new data type in which to store Objects (Type "O" !!)The means to create (instantiate) and manipulate such objectsThe ability to test objects for equality

Note that when we test two objects for equality we are testing to see if they are the exact same object NOT comparing their contents

* This next line defines "JavaString" as an Object of type StringD JavaString S O CLASS(*JAVA:'java.lang.String')

* This is the prototype needed to instantiate a new String objectD MakeJString PR O EXTPROC(*JAVA:'java.lang.String':D *CONSTRUCTOR) D InputString 256A Options(*VarSize)

Note that arrays of objects are allowed, but fields of type 'O' cannot be subfields of a Data Structure

It is important to understand that fields of type 'O' are NOT pointers, although they do store references to objects. Unlike a regular RPGIV field the object does not really exist simply because you have coded its definition in your program. The definition is simply a template. You must "instantiate" the object using its class constructor before you can reference it. Those of you familiar with Java will recognize this behavior but it may not be obvious to those who have not been exposed to the Java language. Think of the definition of the object as being like the DDS definition of a record - compiling the DDS does not place a record in the file, you must "instantiate" the record by writing it to the file.

Currently the first parameter must be specified as *Java, but now (V5R1) that the C++ compiler is available to all AS/400 and iSeries application developers we may see greater C++ usage and the compiler will no doubt be updated to allow interfacing to C++.

Note that the method name must be fully qualified and that the special method name of *CONSTRUCTOR is used to create a new instance.

Even if you never intend to write a line of Java, there may be others in your shop who will. Writing Native Methods for them in RPGIV can provide them with some very powerful extensions to their Java toolkit and allow for significant code reuse. Why write it from scratch in Java using JDBC etc. when you can use your high-speed RPG code!

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 29-30

Coding RPGIV Native Methods

H NoMain Thread(*Serialize) FProduct1 IF E K Disk EXTFILE('ALLNNFL/PRODUCT1')FCATEGOR1 IF E K Disk EXTFILE('ALLNNFL/CATEGOR1')

* Prototype for the new RPG "method" getDescription D getDescription PR 50A EXTPROC(*JAVA:'ProductClass'D :'getDescription')D prodCode 7A CONST

* Proc. interface for getDescription - just a regular RPGIV subproc!P getDescription B ExportD PI 50AD prodCode 7A CONST

* Definitions for local working variables D ProductDesc S 50A Varying

The prototype should look familiarIt is similar to the prototype for a conventional subprocedureThe exception is the Java information in the EXTPROC keyword

The class name ProductClass ties the method to the parent class Note use of Thread(*Serialize) for safe Java/RPG inter-operability

As you can see, when defining a Java Native Method we use the same form of the EXTPROC keyword that we use when invoking Java methods. That is the only real difference that we have to code for.

Under the hood, there is a lot more going on. For example it is necessary to transform the RPG character string into a Java byte string. Don't worry though - this is all taken care of by the compiler.

Our subprocedure, together with any others that we wish to use as Native Methods, must be bound into a Service Program. This Service Program will be loaded by the Java code. From then on, the RPG code is used just like any other method. We almost .... there is one small syntax difference which we will see when we look at the Java code.

It is beyond the scope of these notes to get into the details of why Thread(*Serialize) is needed for safe Java/RPG inter-operability, but you can either just take our word on it, or read up on the subject and then take our word on it <grin>. Either way you should understand the full implications of Java/RPG inter-operability before committing applications to production.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 31-32

C ProdCode Chain Product1

C If %Found(Product1)C Eval ProductDesc = %Trim(ProdDs)C CatCod Chain Categor1

C If %Found(Categor1)C Eval ProductDesc = ProductDescC + '(' + %Trim(CatDes) + ')'C ElseC Eval ProductDesc = ProductDesc + '(Category 'C + CatCod + ' Not found)'C EndIfC ElseC Eval ProductDesc = 'Product ' + ProdCodeC + ' Not found'C EndIf

C Return ProductDesc P getDescription E

Coding RPGIV Native Methods This page shows the actual subprocedure logic for our example

Just regular old RPG - nothing special

As can be seen from this example, the RPG logic comprising our new Java Native method is no different than it would be if we were writing it to be called from RPG. The differences (as you saw on the previous chart) are in the prototype and procedure interface.

If it were essential to pass back to the Java program a Java object (for example a String class object) we could do so by invoking the required methods and returning the object. It is probably better however to simply pass back an RPG data type (as we do in this example) and have the Java program perform whatever typecasts may be necessary.

Because of this "undercover" work, we cannot call a Native method directly from RPG. If we want to have the same subprocedures available to our regular RPG code we should code them in the normal way and then provide a second subprocedure that will "wrap" them as Native methods.

Note that our example is really incomplete since every NOMAIN procedure that uses files should provide a method of explicitly closing them (the compiler will warn about this). Obviously that subprocedure should also be surfaced as a Java Native Method to allow the Java program to properly close the files.

The following charts describe the basic elements that need to be coded in the Java application in order to us our Native Method.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 33-34

public class ProductClass {

// Prototype for Native RPG method

native byte[] getDescription (byte orderNumber[]); static{ try {

System.loadLibrary("TESTRPGJNI"); // Load Native Method SRVPGM } catch (UnsatisfiedLinkError e1){

// Cannot find the service program. Write error message & exitSystem.out.println("I did not find TESTRPGJNI *SRVPGM.");System.exit(1);

}}

Declaring the Native Method Before using the method the Java class must:

Specify a prototype for the method - Note the native keywordLoad the required Service Program

System.loadLibrary takes care of this

Invoking the Native Method

public void displayOrderInfo() {

byte result[];

// invoke the RPGIV method

result = getDescription(str.getBytes());

// Print out the result

System.out.println("The description of product number " + str + " is:");

System.out.println(result.toString());}

The method is invoked just like any otherThe fact that it is coded in RPG makes no difference at this point

A great opportunity for collaborationThe RPG programmers write the business logic that accesses the OS/400 database and processes the business rulesThe Java programmers write the User Interface

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 35-36

Loading Java Methods from RPGTo create a Java object we must do two things:

Define a data item of type Object (O)Note that this does not cause the object to exist - it is just a "template"

Create the object using the *CONSTRUCTOR method of the classThis actually allocates the storage etc. and makes it a "real" object

H DftActGrp(*No)

D EMAilAddr S 30A INZ(' [email protected] ')

D JEMailAddr S O CLASS(*JAVA:'java.lang.String')

D MakeJString PR O EXTPROC(*JAVA:'java.lang.String'D :*CONSTRUCTOR)D InputString 256A Options(*VarSize)

........... additional prototypes shown on next page .................

* Display content of EMailAddr to show leading blanksC EMailAddr DSPLY * Instantiate JEMailADDR Using EMailAddr to supply the initial valueC EVAL JEMailAddr = MakeJString(EMailAddr)

This is the complete code for the example on the previous and following pages: H DftActGrp(*No) D EMAilAddr S 30A INZ(' [email protected] ') D JEMailAddr S O CLASS(*JAVA:'java.lang.String')

D MakeJString PR O EXTPROC(*JAVA:'java.lang.String': D *CONSTRUCTOR) D InputString 256A Options(*VarSize) D TrimJString PR O EXTPROC(*JAVA:'java.lang.String': D 'trim') Class(*Java: D 'java.lang.String') D JStringToAlpha PR 10A EXTPROC(*JAVA:'java.lang.String': D 'getBytes') * Display content of EMailAddr to show leading blanks C EMailAddr DSPLY * Instantiate JEMailADDR Using EMailAddr to supply the initial value C EVAL JEMailAddr = MakeJString(EMailAddr) * Call the trim method to strip leading and trailing blanks from the String C EVAL JEMailAddr = TrimJString(JEMailAddr) * Now convert the Java String back to a conventional RPG string C EVAL EMailAddr = JStringToAlpha(JEMailAddr) * and display the result to show that the leading blanks have disappeared C EMailAddr DSPLY

C Eval *InLr = *On

Remember that the object JEMailAddr must be intstantiated before it can be used. Simply defining it is not enough. Since the *CONSTRUCTOR method allows us to supply an initial value we can also "load" its content at the same time.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 37-38

.............................................................D TrimJString PR O EXTPROC(*JAVA:'java.lang.String'D :'trim') Class(*JavaD :'java.lang.String')

D JStringToAlpha PR 10A EXTPROC(*JAVA:'java.lang.String'D :'getBytes')

............................................................. * Call the trim method to strip blanks from the StringC EVAL JEMailAddr = TrimJString(JEMailAddr)

* Now convert the Java String back to a conventional RPG stringC EVAL EMailAddr = JStringToAlpha(JEMailAddr)

* Display the result to show that the leading blanks have disappearedC EMailAddr DSPLY

Once the object has been created we can manipulate itIn our example we have applied the trim and getbytes methods

In the end, EMailAddr contains '[email protected]' The same effect as EVAL EMailAddr = %Trim(EMailAddr)

Invoking Java Methods from RPG

The bullet on the chart "The same effect as EVAL EMailAddr = %Trim(EMailAddr)" is there to give you a laugh and ensure that you understand that this is a trivial example - nobody in their right mind is going to use Java methods to perform a function for which RPGIV provides excellent native facilities.

The example is intended to demonstrate the mechanics of using Java methods in RPGIV and is therefore of necessity somewhat simplistic (not to mention silly). However, there may be times when we want to use Java methods that require that we create and pass String, and other Java objects, as parameters. The same principles shown here will also apply to those cases.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 39-40

Writing RPG Native methods and invoking them from JavaThere are no major problems here

Although you need to be aware of the issues when multithreaded Java applications are in use

Creating and manipulating Java objects from RPGThere are many traps for the unwary

For example - Java garbage collection cannot "clean up" your old objects without your help.

Since RPG created them the JVM does not know they exist

Check the manual for further detailsYou will find them in the Programmers Guide

Under the heading "RPG and Java" Chapter "RPG and the eBusiness World"

Proceed with Caution !!

While it is fine to "play" with this support, you should give very serious consideration before rushing in and using it in a production environment. There are a number of limitations and considerations that you should make yourself familiar with. For example, any Java Object created by RPG must also be "cleaned up" by RPG code. The Java garbage collector does not know about the existence of these objects and is unable to handle them.

The "RPG and Java" section in the "RPG and the eBusiness World" chapter of the Programmers Guide is essential reading before you attempt to use this support in production code.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 41-42

Calculation Specs can now be completely free-formEach group of free-form specs starts with a /FREE entry

And ends with /END-FREE (optional)Opcode and operands can appear anywhere in posns 8 - 80Free-form may be interspersed with traditional C-Specs

Not all Op-codes are supportedMore on this laterSome such as CALLP and EVAL are optional

Except where an extender is needed

Free Form C-Specs

/Free If CustomerNumber = *Blanks; CustomerError = True; Else; Eval CustomerError = False; EndIf; /End-Free

Free!!I'm Free!!

Probably the most significant thing about using Free form calcs is that you can indent your source statements logically, as shown in this example.

Note the use of the semicolon (;) to specify the end of the statement.

In the code example, note that in the statement following the If, we omitted the EVAL operation code entirely. In contrast, in the statement following the ELSE, we included the EVAL. Since it is optional, either option will work.

Although free-form can be mixed with traditional C specs, the result is code that is ugly and hard to maintain. Replace calc lines with options that will work in free form instead.

Just in case I forget to mention it during the session, although there is no OS/400 based "convert to free form" utility that doesn't mean you have to do it all by hand. Check out the last page of the handout for details of the options that we have encountered so far.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 43-44

Op-Code is followed by Factor 1, 2, and the Result FieldOperands are no longer limited to 14 charactersNo blanks allowed between Op-code and Extender(s)Each statement must end with a semicolon (;)Only 1 Op-code can be coded on a line

Comments are delimited by //And may be placed at the end of any free-form statement (after the ;)

Fields may not be defined in Free-form specificationsBut then you wouldn't want to do such a naughty thing anyway!

Resulting indicators may not be specifiedThere are built-in functions that can be used instead

Some Op-codes are not currently supportedMore on this later

Free Form C-Specs Rules

If you are not in a position to move to V5 yet, there are a number of steps you can take to prepare yourself for the change. These are all good programming style anyway so you can't lose!

Use only those opcodes supported by freeformDefining all variables on D-SpecsDo NOT use conditioning indicatorsAvoid resulting indicators whenever possible. If you have to use one (for Opcodes such as LOOKUP) then _only_ use them to control the operation itself. Use BIFs such as %Found to check the results.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 45-46

/FREE READ File; // Read file to prime do loop DOW not %EOF(File); // Continue until all records processed IF %Error; DSPLY 'The read failed'; LEAVE; ELSE; CHAIN(N) name database data; Name = %Xlate(Upper : Lower : Name); EXSR Handle_record; READ File; ENDIF; ENDDO;

BEGSR Handle_record; EVAL(H) Time = time + Total_hours_array (EmpNo); Temp_hours = Total_hours - Excess_hours; Record_transaction(); ENDSR; /END-FREE

Free-form Example

Notice the use of // for end of line comments

Note: EVAL is required because

of the Extender

Note again the benefit of indentation of the source. With the source coded this way, it is very clear which statements belong to the If block and the Else block. And it is clear that that the IF/ELSE block is all part of the DOW.

Notice that we must use the %EOF and %Error built-in functions because resulting indicators cannot be used in free-form calcs. That's OK, because even in fixed format source, the use of the BIFs makes the code far more readable and understandable.

Notice that in this code sample, the EVAL operation code has been omitted - except where it was necessary to include it because of the half adjust extender.

Now for a tough question: Assuming these calcs are syntactically correct (ie, the program containing these calcs will compile), what could Record_transaction() be? Is it an array element? A subprocedure call? A program call?

It is either an subprocedure or a program call (or, to be more specific, it is the name on the prototype given to a subprocedure or program to be called.) The CALLP (call with prototype) operation code, like the EVAL, can be omitted, as it is in this case.

You see another change in syntax for Version 5 in that same line, because prior to V5, if the procedure or program required no parameters to be passed, we could NOT have coded the empty parentheses. It is a good idea to use the empty parentheses notation so the name is not mistaken for a field name. In a free form spec, it is required to use the empty parentheses when no parameters are being passed.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 47-48

Op-codes not supported in /Free format These fall into five main categories

"Old fashioned" Op-codes whose use is discouragede.g. ANDXX, COMPxx, DEFINE, DOxyy, GOTO, IFxx and MxxZO

Those for which new BIFs have been providede.g. ALLOC, CHECK, EXTRCT, LOOKUP, TIME and XLATE

Op-codes with existing (if not identical) expression support e.g. ADD, DIV, MULT and SUB

Those supported by a combination of enhanced and new supporte.g. ADDUR,

"Problem Children" - Op-codes that caused problems in implementation and/or were too infrequently used to justify the effort

Support for these may be added in the futureExamples include: KFLD, KLIST, MxxZO and TESTN

The notes list the unsupported Op-codes

NOTE: These Opcode substitutions are not necessarily one-to-one. The substitute might not work exactly as the original code, especially in the area of error handling. In other words, you should view the free-form calcs as being primarily aimed at new code, not for conversion of existing code.

Op-Code /FREE Substitute

ADD operator +ADDDUR operator +ALLOC BIF %ALLOCANDxx Operator ANDBlTxx (none yet)CABxx (see GOTO)CALL opcode CALLPCALLB opcode CALLPCASxx opcode IF with EXSR CAT operator +CHECK BIF %CHECKCHECKR BIF %CHECKRCOMP operators =, <>, <, >, <=, or >=DEFINE LIKE or DTAARA on D-SpecDIV operator / or BIF %DIVDO opcode FORDOUxx opcode DOUDOWxx opcode DOWEND opcodes ENDDO, ENDIF, etc.EXTRCT BIF %EXTRACTGOTO LEAVE, ITER, LEAVESR, RETURN, etc.IFxx IFKFLD (See KLIST)KLIST (None - but in future may use KL structure)LOOKUP %LOOKUPxx or %TLOOKUPxxMxxZO (none)MOVE EVALR or %DATE, %TIME, etc.MOVEA Use overlapping subfields MOVEL EVAL or BIF %DATE, %TIME, etc. MULT Operator *MVR BIF %REM

Op-Code /FREE Substitute

OCCUR BIF %OCCURORxx operator ORPARM (see PLIST)PLIST D-Spec PR definitionREALLOC BIF %REALLOCSCAN BIF %SCANSETON EVAL *lnxx = *ONSETOFF EVAL *lnxx = *OFFSHTDN BIF %SHTDNSQRT BIF %SQRTSUB operator -SUBDUR operator - or BIF %DIFFSUBST BIF %SUBSTTAG (see GOTO)TESTB (none yet)TESTN (none yet)TESTZ (none yet)TIME BIF %DATE, %TIME, or %TIMESTAMPWHENxx opcode WHENXFOOT BIF %XFOOTXLATE BIF %XLATEZ-ADD opcode EVALZ-SUB opcode EVAL

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 49-50

When FILE2 is opened, the file used will be determined by the content of the field 'File2Name'. The member to be

opened will be determined by the content of the field 'Mbr'

FFILE1 IF F 10 DISK EXTFILE('TESTLIB/QRPGLESRC')

FFILE2 IF F 10 DISK EXTFILE(File2Name) EXTMBR(Mbr)

Runtime control of file opensEXTFILE to override file name, EXTMBR for member name

can specify a literal or a field nameThe library name may be specified as *LIBL (the default)The member name may be specified as *FIRST (the default) or *ALL

When FILE1 is opened, the actual file used will be the file TESTLIB/QRPGLESRC

(first member)

The file name may be library-qualified, but it is not required.

Note that both file and member names are CASE SENSITIVE! Since database, display and printer files are all currently upper case in OS/400, all names should be specified in upper case. In our code example here, if the field File2Name were to contain a value of 'MyLib/File2', the open would fail because the file and library name would be not found. The value would need to be 'MYLIB/FILE2' instead.

If a variable name is used in these keywords, the appropriate value must be set prior to the file open. We have examples of how that might work on the following chart.

It is important to note that the EXTFILE keyword is NOT used to find the file a compile time for this program. So it may still be necessary in some cases to use an override command in CL prior to the compile.

What is the impact of a CL override command when used in combination with EXTFILE? The CL override WILL be in effect IF it was issued for the actual file that RPG opens. For example, if the File2Name field has the value MYLIB/MYFILE at runtime AND the command OVRDBF MYFILE OTHERLIB/OTHERFILE has been used, the actual file opened will be OTHERLIB/OTHERFILE.Note that any overrides for the name FILE2 will be ignored.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 51-52

FFile2 IF F 10 DISK EXTFILE(FileName2)

FPrinter IF F 10 DISK EXTFILE(PrtName) USROPN

D PrtName S 21AD FileName2 S 21AD Testing S N

C *ENTRY PLISTC PARM FileName2 C PARM Testing

* Use test library print file when testingC IF Not TestingC EVAL PrtName = 'PRODLIB/PRTFILE'C ELSEC EVAL PrtName = 'TESTLIB/PRTFILE'C ENDIF

C OPEN Printer

Example of Runtime file controlName to be used for File2 is supplied as a parmName for file Printer is supplied by calculation logic

So file must be defined as USROPN

If a file is not specified as UsrOpn, then one of the following can be used to set the value of the variable:

Use the INZ keyword on the D specification Pass the value in as an entry parameter Use an EXPORT/IMPORT variable whose value is set by another module

Note that perhaps a better way of doing this kind of conditional code for testing purposes would be to use conditional compiler directives instead of the If/Else logic shown here. We used this type of example so as not to confuse those of you who are familiar with conditional compiler directives. An alternative coding technique might be:

/If Defined(Testing) C EVAL PrtName = 'PRODLIB/PRTFILE' /Else C EVAL PrtName = 'TESTLIB/PRTFILE' /EndIF

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 53-54

ELSEIF Operation Code

* The "Old" wayC IF Count < PageMax * :C ELSEC IF LastPage * :C ELSE * :C ENDIFC ENDIF * The "New" way using ELSEIF C IF Count < PageMax * :C ELSEIF LastPage * :C ELSE * :C ENDIF

Effect is the same as SELECT / WHEN / OTHER

The nicest thing about ELSEIF is that you do not need the long series of ENDIFs at the end of very complicated nested IF logic.

If you are wondering "Why wouldn't you just use SELECT/WHEN/OTHER logic instead of IF/ELSEIF logic?" The answer is that we probably would. But many programmers just seem to be more comfortable with IF/ELSE type logic than SELECT/WHEN. It's really a matter of personal preference and style.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 55-56

D DateInfo DS Qualified D Year 5I 0D Month 5I 0D Day 5I 0D MonthName 10A Varying

D OrderDate DS LikeDS(DateInfo)

Qualified Data NamesFor the first time RPG supports qualified data names

Two new keywordsQUALIFIED

Means that all references to fields in that DS must be qualifiedLIKEDS

Means the DS inherits all the fields (names & attributes) of the named DSReferences to fields in this DS must also be qualified

Qualification is through dot (.) notation as in C and JavaDSName.FieldName

e.g. DateInfo.Year, OrderDate.MonthName

When a DS is qualified, the same name can appear in multiple Data Structures. Qualified data names are particularly useful when you want to /Copy some code containing standard Data Structures because even if any of the subfield names happen to already exist in the program, they will not cause an error.

There is also a new parameter value allowed on the INZ keyword for data structures defined with the LIKEDS keyword: INZ(*LIKEDS). This means that any initial values specified in the original data structure should be replicated in the new (LIKEDS) data structure as well. For example, if in our code sample on this chart had an initial value for the Year subfield in DateInfo, such as INZ(2001), that initial value would ONLY be carried over to the OrderDate.Year field IF we added the keyword INZ(*LIKEDS) to the OrderDate D spec.

There is a particularly good discussion and example of this support as it relates to passing a data structure as a prototyped parameter in the article by Hans Boldt and Barbara Morris in IBM's iSEries Magazine, May 2001 issue. See the notes earlier in this handout for the URL where you can get a copy of this article if you don't have the magazine.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 57-58

D DateInfo DS QUALIFIED D Year 5I 0D Month 5I 0D Day 5I 0D MonthName 10A Varying

D OrderDate DS LIKEDS(DateInfo)

/free // Determine day, month, and year OrderDate.Day = %SubDt(Date:*D); OrderDate.Month = %Subdt(Date:*M); OrderDate.Year = %Subdt(Date:*Y);

// Get month name OrderDate.MonthName = Months(OrderDate.Month); /end-free

Qualified Data NamesOrderDate is LIKEDS(DateInfo)

DateInfo has the keyword QUALIFIED Therefore to use fields in DateInfo or OrderDate they must be qualified by their respective DS names

Notice that in this example, we did not actually refer to the original DS subfields anywhere (i.e., there are no references to DateInfo.Year, for example). So this example is not complex enough to illustrate the power of this support. Imagine, for example, that we may also want a data structure containing the Ship Date and the Invoice Date and maybe Todays Date as well as the Order Date. Now you can see how nice it would is to have the LIKEDS ability to easily replicate the entire structure of a data structure many times in a program.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 59-60

Other EnhancementsOpcode DUMP supports new extender (A)

Specifies that DUMP operation is A(lways) to take placeRegardless of Debug option on the H spec.

Calls to Functions/Procedures/Programs with no parametersEmpty parentheses now allowedMuch easier to distinguish such calls from simple variable names

Predefined Conditions - conditional compiler directives*VnRnMn (e.g. V3R7M0)

Condition is set if specified release is supported by the compiler*ILERPG

Set when compiling on the AS/400 but not for VARPG*CRTBNDRPG *CRTRPGMOD

Set based on compile option used

We have already seen an example of the empty parentheses for prototyped calls with no parameters in the free format example earlier.

Conditional Compilation Directives were introduced into RPG in V3R7. At that time, only programmer-defined conditions could be specified. In V5, predefined conditions have been added.

The *CRTBNDRPG and *CRTRPGMOD conditions come in handy when there is some standard code in many source members - some that is compiled with CRTBNDRPG and some with CRTRPGMOD. For example, you may have H spec lines that have keywords, such as DFTACTGRP(*NO) that are only supported when using the CRTBNDRPG command. So you could condition that H spec line to be included only when that command was used for compilation.

The version and release condition can be very useful if you need to compile your code to different releases. If you want to use a function that is only supported in a recent release, but you may also need to support previous releases, you can put both versions of the source into your program and conditionally compile the correct version based on the selection for target release.

If you use VisualAge RPG and you share some of your logic between your VARPG and OS/400 RPG applications, the *ILERPG can be useful to differentiate "host only" code.

If you are not familiar with Conditional Compilation Directives, you should read about them in the ILE RPG Programmer's Guide to understand the context of the new predefined conditions. Conditional Compilation Directives were introduced in V3R7, but there were no predefined conditions - only those that the programmers defined themselves, using /DEFINE. With V5R1 comes the first time the compiler has created conditions for us.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 61-62

Other Enhancements%PAddr

Now allows you to specify a Prototype name as the argument

Relaxed rules on DIM, OCCURS and PERRCDParameter no longer needs to be previously defined

New directive /INCLUDEFunctions as a /COPY but is not expanded by the SQL preprocessor

Avoids problems caused by SQL failing to understand some RPG IV options

Relaxed rules on %SCANLength of search argument can be greater than the string being searched

OFLIND parameter can now be a named indicatorCan you hear those nails being pounded into the *INnn coffin!

The %PADDR (procedure address) enhancement allows a prototype name to be specified as a parameter. Previously, you specified the procedure name to call and since they are case sensitive, it could be error prone.

While the length of the search string in %SCAN can now be longer than the string searched (i.e., you will not get a run time error), note that the string will never be found in this case.

If you use embedded SQL, you may find /INCLUDE gets around some of the problems that you have likely had due to the SQL precompiler's lagging behind in understanding new RPG IV features. Any functions that SQL can't understand (and are not critical specifically to the SQL statements themselves) can be put into a separate member and copied in at compile time WITHOUT the SQL precompiler seeing them. It can prevent errors in the pre-compile phase.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 63-64

Related Web Links etc.iSeries Magazine article on V5 RPG IV

Written by Hans Boldt and Barbara MorrisThe RPG IV compiler architects

www.the400resource.com/content/monthly/200105/rpg5.html

Craig Rutledge's commentary and utilitiesUtilities to assist in converting to /Free formatwww.alltel.net/~craigru/jcrcmd2.html & follow the "Path to /Free" link

I don't agree with all his comments but there is useful stuff here

Linoma's RPG ToolboxThe latest evolution of the first RPG III conversion utility

Now accommodates /Free formatting and much morewww.linomasoftware.com and follow the RPG Toolbox links

Continuation of notes on MONITOR:

The idea of the second MONITOR example is that we are processing a file which (for the sake of argument) came from an outside source (e.g. transfer from a PC, UNIX box, whatever). Past experience with this file has shown that the numeric data is corrupted from time-to-time. Obviously I could test the data before operating on it, but even if there was a simple method of doing this (which there isn't because TESTN doesn't really work very well) the time taken to test millions of records when perhaps only 4 or 5 are corrupted would be too great an overhead.

The program is not intended to be "real" just to demonstrate technique. We start by setting the flag DataIsClean to *OFF i.e. data is not yet clean. We then read a record and enter a DO loop that will only terminate when DataIsClean is true. Each of the numeric fields is processed in turn with a flag being set prior to each step so we know which field is the one causing the problem (if there is one).

OK - that's the outline of what is going to happen - let's walk through the process.

Case 1: All numeric fields are valid.

All of the processing will proceed normally with no errors. Because of this the Eval DataIsClean = *ON is processed and the inner DO loop exits. The next record is read and the process starts over.

Case 2: One or more fields have errors.

For the sake of this example, let us assume that field 2 is a valid number, but that fields 1 and 3 have been corrupted.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 65-66

Continuation of notes on MONITOR:

Case 2: (Contd.)

The first numeric field is processed and a decimal data error results. Processing passes to the first (and in fact only) ON-ERROR which executes the Clean up routine. The clean up can do anything you like - in my simple example I have it use the "Processing" value to determine which field to set to zero. You would normally do reporting or whatever else you need here.

On completion of the clean up routine the DO loop returns control to the top again and we reprocess the first field. This time it is OK since we "cleaned" it. The second field works just fine as well, but when we reach the third things go "boom" again. Once more control goes to the ON-ERROR and to the clean up routine which this time knows to process field 3.

When we return, we go back and do it all over again starting with field 1. Because all data is now "clean" we will eventually reach the point where we set DataIsClean to *On and exit the DO loop.

Hopefully this explains it well enough for you to see what is going on. The critical point to note is that in this case the MONITOR must be _inside_ the DO loop. Monitor effectively sets a switch that is turned off as soon as an error has been processed. If Monitor was outside the DO loop we could only detect a single error, the others would just blow up in the old-fashioned way.

In a "real world" situation I would probably use a subprocedure instead of a subroutine for the fix-up/reporting. This would allow me to have it return a fixed/not fixed flag. I could then test for this condition and reprocess the data if the field had been fixed (as the current example does) or simply terminate processing for this record and go on to process the next one.

(c) 2002 by Partner400 RPG IV - V5R1 Features - Page - 67-68