× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.



Scott,
  I have tried testing a very similar strategy. I did like it but for I did
not know about CEE4RAGE. Also I was using monitor instead of %open and I did
not feel that it was a good way, as in my done() procedure I try to close
and catch for an error code using monitor.

  But I have one question. If we open all the files used by various
procedures in the module, when only one procedure is used by the caller,
What is the need of defining the file as usropn, Doesn't the OS do it for us
when we declare a file with out usropn in a NoMain module?

I know we cannot code CEE4RAGE, and depend on the OS to close all ODP's when
activation group ends. What are the other advantages?

-Praveen

On 2/21/06, Scott Klement <rpg400-l@xxxxxxxxxxxxxxxx> wrote:
>
>
> > 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 list
>
> To 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...
> --
> This is the RPG programming on the AS400 / iSeries (RPG400-L) mailing list
> To post a message email: RPG400-L@xxxxxxxxxxxx
> To subscribe, unsubscribe, or change list options,
> visit: http://lists.midrange.com/mailman/listinfo/rpg400-l
> or email: RPG400-L-request@xxxxxxxxxxxx
> Before posting, please take a moment to review the archives
> at http://archive.midrange.com/rpg400-l.
>
>

As an Amazon Associate we earn from qualifying purchases.

This thread ...

Follow-Ups:
Replies:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

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.