|
What is the best way to use Activation groups? I am really worried about file access.
There's no "One True Way" of using activation groups. You should use them as they are appropriate for your application.
I have a lot of Utility procedures with core business logic that is right now repeated in most of the programs. I am trying a progressive approach as to when ever we change a particular program find the generalizable procedures and put it in a service program. Change the program to use these procedures. Also I have made all the utility Service Programs to run in one activation group.
Unless you have a good reason not to, I'd suggest always putting a service program in ACTGRP(*CALLER).
A service prgoram is intended to provide services to the program that calls it. That's why it should always run in the same activation group as the program that calls it. It's the caller who should be deciding on the activation group.
There are exceptions, however. For example, when a service program is called from Java, I prefer to put it in a named activation group. Otherwise, the Java program (which isn't ILE and knows nothing about ACTGRPs) may load it into the wrong place.
The major question I have is: Should I use it with OVRDBF ..... Share(*YES) ? What are the advantages over not using Share(*YES) Also how do I control open and close of files.. I do not want to open and close a file each time a procedure is called.
These questions aren't related to activation groups (at least not directly), but I'll answer them anyway.
Should you use SHARE(*YES)? I don't know... do you need share the ODP between more than one program? In other words, do you want a SETLL in one program to affect the record that gets read in a different program? If so, specify SHARE(*YES). If not, specify SHARE(*NO) which is the default.
How do you control open and close of files? I prefer to open everything when my service program is called for the first time, and then leave them open until the activation group ends (or the caller specifically asks for them to be closed)
To implement this, I add "open" and "close" subprocedures to my service program.
Consider a simple example... You want to write a module that works with customer information. To start with, it'll have three main subprocedures:
cust_getAddr() -- gets the address information for a customer. cust_startOrdList() -- start a list of orders for customer cust_readOrdList() -- read the next order from the listTo make these subprocedures work, you'll need two files. CUSTMAS which as the customer's address info in it, and ORDBYCUST which is a logical of the orders file by customer number.
To open & close the files, I'll add two more subprocedures: cust_init() -- initialize service program (open files) cust_done() -- done with service program (close files)Note that I don't want my caller to have to call these. If for some reason the calling program NEEDS to be able to control when the files are opened and when they're closed, I'll have it call the above subprocedures. But I want them to be optional -- the caller ONLY calls them if it needs to. Otherwise, it can just call cust_getAddr() and cust_startOrdList() and the files will "magically" be opened before they're used.
I'll also have a subprocedure that I can call to get an error message, in case something should go wrong. That subproc will be
cust_error() -- get error message (and optionally error number) Here's a sample of how I might implement this service program: H NOMAIN FCUSTFILE IF E K DISK USROPN FORDBYCUST IF E K DISK USROPN /copy prototypes,custr4 D CEE4RAGE PR D procedure * procptr const D feedback 12A options(*omit) D SetError PR D ErrNo 10I 0 value D Msg 80A varying const D Initialized s 1N inz(*OFF) D LastCust s 8A D save_Errno s 10I 0 inz(0) D save_ErrMsg s 80A varying D inz('No Error') *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_init(): Initialize customer module * Note: If you don't call this manually, it'll be called * automatically when you call another subprocedure *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P cust_Init B export D cust_Init PI /free if (Initialized); return; endif; open CUSTFILE; open ORDBYCUST; CEE4RAGE( %paddr(Cust_Done): *omit ); Initialized = *on; /end-free P E *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_Done(): Done with module * Note: If you don't call this manually, it'll be called * automatically when the activation group is reclaimed *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P cust_Done B export D cust_Done PI /free if %open(CUSTFILE); close CUSTFILE; endif; if %open(ORDBYCUST); close ORDBYCUST; endif; Initialized = *OFF; /end-free P E *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_getAddr(): Get a customer's address * * CustNo = (input) customer number to retrieve * Addr = (output) Customer's address * * returns *ON if successful, *OFF otherwise *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P cust_getAddr B export D cust_getAddr PI 1N D CustNo 8A const D Addr likeds(Cust_address_t) D Err s 10I 0 /free Cust_Init(); chain(e) CustNo CUSTFILE; if %error; err = %status(); SetError(CUST_ECHNERR: 'RPG status ' + %char(err) + ' on CHAIN operation.'); return *OFF; endif; if not %found; SetError(CUST_ENOTFND: 'Customer Not Found'); return *OFF; endif; Addr.Name = Name; Addr.Street = Street; Addr.City = City; Addr.State = State; Addr.ZipCode = ZipCode; return *ON; /end-free P E *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_StartOrdList(): Start a list of orders for a customer * * CustNo = (input) Customer to get orders for *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P cust_StartOrdList... P B Export D cust_StartOrdList... D PI 1N D CustNo 8A const /free Cust_Init(); setll CustNo ORDBYCUST; if not %equal; SetError(CUST_ENOORDS: 'No Orders Found for Cust ' + CustNo ); return *OFF; endif; LastCust = CustNo; return *ON; /end-free P E *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_ReadOrdList(): Get next order from order list * * Ord = (output) Order number of next order * * Returns *ON if successful, or *OFF at the end of the list *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P cust_ReadOrdList... P B Export D cust_ReadOrdList... D PI 1N D Ord 5A /free Cust_Init(); reade LastCust ORDBYCUST; if %eof; return *OFF; endif; Ord = OrderNo; return *ON; /end-free P E *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_Error(): Get last error that occurred in this module * * ErrNo = (output/optional) Error number * * Returns the last error message *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P cust_Error B Export D cust_Error PI 80A varying D ErrNo 10I 0 options(*nopass:*omit) /free Cust_Init(); if %parms>=1 and %addr(Errno)<>*NULL; ErrNo = save_Errno; endif; return save_ErrMsg; /end-free P E *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * SetError(): (INTERNAL) set the error number and message *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P SetError B D SetError PI D ErrNo 10I 0 value D Msg 80A varying const /free save_Errno = Errno; save_ErrMsg = Msg; /end-free P E Note the following: a) cust_getAddr() and cust_startOrdList() call cust_init() to open the files. Since that subprocedure will do nothing if the Initialized variable is *ON, the files won't get opened twice. b) cust_init() uses the CEE4RAGE() API to register a subprocedure that will be automatically called when the activation group is ended. It registers cust_done() so that the files can be closed. (Technically, this is unnecessary, since files are closed automatically when an actgrp ends -- however, I personally like to have code that explicitly closes the files that's visible in my code. Plus, you might create a temporary file in QTEMP or a temporary user space, or something like that, that should be deleted when the actgrp ends -- this gives you the chance to do that, or any other code you want to run when the caller is done.) c) Because cust_init() is called by cust_getAddr() and cust_startOrdList(), my caller doesn't HAVE to call it. Likewise, because cust_done() will automatically be called when the actgrp ends, the caller doesn't HAVE to call it. d) However, if the caller WANTS my files to close without reclaiming the ACTGRP, or he wants them to be closed and then re-opened, he has the option of calling cust_init() or cust_done(), making things a little more flexible. e) The error constants, and customer address data structure are defined with the prototypes in the /COPY member. I always take pains to start everything that's in that /COPY member with a common prefix. In this case I started everything with CUST_. If all of my modules/srvpgm's follow this convention, I won't run into trouble with the same defintions popping up for another module. f) Since my module is called CUSTR4 (for CUST, RPG IV) it'll be easy for the caller to understand where the code is. Anytime he sees a call to a CUST_ routine, he'll know it's calling something in the CUSTR4 module. Here's what the /COPY member looks like: /if defined(CUST_PROTOTYPE_DEFINED) /eof /endif /define (CUST_PROTOTYPE_DEFINED) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_init(): Initialize customer module * Note: If you don't call this manually, it'll be called * automatically when you call another subprocedure *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ D cust_Init PR *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_Done(): Done with module * Note: If you don't call this manually, it'll be called * automatically when the activation group is reclaimed *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ D cust_Done PR *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_getAddr(): Get a customer's address * * CustNo = (input) customer number to retrieve * Addr = (output) Customer's address * * returns *ON if successful, *OFF otherwise *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ D cust_getAddr PR 1N D CustNo 8A const D Addr likeds(Cust_address_t) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_StartOrdList(): Start a list of orders for a customer * * CustNo = (input) Customer to get orders for *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ D cust_StartOrdList... D PR 1N D CustNo 8A const *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_ReadOrdList(): Get next order from order list * * Ord = (output) Order number of next order * * Returns *ON if successful, or *OFF at the end of the list *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ D cust_ReadOrdList... D PR 1N D Ord 5A *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * cust_Error(): Get last error that occurred in this module * * ErrNo = (output/optional) Error number * * Returns the last error message *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ D cust_Error PR 80A varying D ErrNo 10I 0 options(*nopass:*omit) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * Data structure that contains a customer's address *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ D CUST_Address_t DS qualified D based(TEMPLATE) D Name 25A D Street 30A D City 15A D State 2A D ZipCode 9S 0 * * Error message numbers: * D CUST_ECHNERR C 1101 D CUST_ENOTFND C 1102 D CUST_ENOORDS C 1103 Hope that helps...
As an Amazon Associate we earn from qualifying purchases.
This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].
Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.