× 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.



In the case of database maintenance applications, I think it's helpful to 
separate code that handles database I/O from screen I/O code.  Applications are 
easier to maintain, when the code is separated.  If there is a problem with 
screen I/O code, there's no need to wade through database I/O code, and visa 
versa.

I'll attach a sample source member for a database I/O module at the end of this 
message.  I normally create individual service programs from database I/O 
modules.

In database maintenance applications, I use SQL for set oriented operations 
(queries and lists), but I still have a strong preference for traditional RPG 
operation codes for record-level access.  Performance is much better, the data 
is always current, and there's no need for object-relational mapping middleware 
(like Hybernate for J2EE interfaces).

The code I'll append to this message supports a browser based user interface 
that combines a scrollable list with a data entry form on a single screen.  
When users click on one of the records in the list, a request is sent to the 
server to retrieve the complete record from the database file, and update the 
browser's "content" frame.

Users can also press arrow keys to navigate from row to row in the list.  A 
highlight bar moves from row to row when arrow keys are pressed and the content 
frame is updated with a complete view of the record.

If a user presses and holds down an arrow key, the highlight bar moves from row 
to row at a rate of about 25 rows per second, and the server is flooded with 
requests for database I/O and screen I/O, and records fly past in the content 
frame at a rate that's humanly impossible to read.

Users like this level of performance and interactivity, and the only way I can 
conceive of delivering that level of performance is via RPG's 
record-level-access operation codes.

When updating records, I agree that some applications may need to first check 
whether a record was previously updated by another user, and handle an 
exception, but in other applications, the rule may be that the last user to 
update the record is given the final say (configuration files, for example).  A 
basic database I/O model can be extended to handle both situations if needed.



Nathan M. Andelin

------------------------
RPG Database I/O module
------------------------

      //-----------------------------------------------------------------
      // headers
      //-----------------------------------------------------------------

     H CopyRight('2006 Relational Data Corporation') nomain

      //-----------------------------------------------------------------
      // file specifications
      //-----------------------------------------------------------------

     FXTD140P   UF A E           K DISK

      //-----------------------------------------------------------------
      // copy members
      //-----------------------------------------------------------------

      /COPY *LIBL/QMODELSRC,WLMAIND1#1
      /COPY *LIBL/QRPGLESRC,RDMSGAPI#1

      //-----------------------------------------------------------------
      // module level data structures
      //-----------------------------------------------------------------

     D dettKey         DS                  Export
     D  det_key1                     10A
     D  det_key2                     10A
     D  det_key3                     10A

     D msgDs           DS                  Export
     D  msg_type                      1A
     D  msg_text                    128A   Varying

      //-----------------------------------------------------------------
      // record buffers (containing prior and current state of a record)
      //-----------------------------------------------------------------

     D dettrec       E DS                  Extname(XTD140P:XTD140R)
     D                                     Export Prefix(m_)

     D dettrec1      E DS                  Extname(XTD140P:XTD140R)

      //-----------------------------------------------------------------
      // module level variables
      //-----------------------------------------------------------------

     D focusfld        S             10A   Varying Export
     D msg_module      S             10A   Inz('WLMAINT')

      //-----------------------------------------------------------------
      // key lists
      //-----------------------------------------------------------------

     C     kls           Klist
     C                   Kfld                    m_APP
     C                   Kfld                    m_FORM
     C                   Kfld                    m_FIELD

      //-----------------------------------------------------------------
      // initialization / use external application message file
      //-----------------------------------------------------------------

     P d1Init          B                   Export

      /free

       msgFileOpen(msg_module);

           clear dettKey;
           clear dettRec;
           clear dettRec1;

      /end-free

     P d1Init          E

      //-----------------------------------------------------------------
      // clean up
      //-----------------------------------------------------------------

     P d1Term          B                   Export

      /free

       msgFileClose();

      /end-free

     P d1Term          E

      //-----------------------------------------------------------------
      // get record
      //-----------------------------------------------------------------

     P d1GetDettRec    B                   Export
     D d1GetDettRec    PI              N

      /free

       Clear dettrec;
       Clear dettrec1;

       m_APP = det_key1;
       m_FORM = det_key2;
       m_FIELD = det_key3;

       Chain(n) kls XTD140R;

       If not %found;
         msg_type = type_error;
         msg_text = msgGetText(msg_module:1);
         Return *Off;
       Endif;

       dettrec = dettrec1;

       Return *On;

      /end-free

     P d1GetDettRec    E

      //-----------------------------------------------------------------
      // change record
      //-----------------------------------------------------------------

     P d1ChgDettRec    B                   Export
     D d1ChgDettRec    PI              N

      /free

       If not d1VfyDettRec();
         Return *Off;
       Endif;

       Chain kls XTD140R;

       If not %found;
         focusfld = 'TITLE';
         msg_type = type_error;
         msg_text = msgGetText(msg_module:2);
         Return *Off;
       Endif;

       dettrec1 = dettrec;

       Update XTD140R;

       msg_type = type_info;
       msg_text = msgGetText(msg_module:3);

       Return *On;

      /end-free

     P d1ChgDettRec    E

      //-----------------------------------------------------------------
      // add record
      //-----------------------------------------------------------------

     P d1AddDettRec    B                   Export
     D d1AddDettRec    PI              N

      /free

       If not d1VfyDettRec();
         Return *Off;
       Endif;

       Chain(n) kls XTD140R;

       If %found;
         focusfld = 'APP';
         msg_type = type_error;
         msg_text = msgGetText(msg_module:4);
         Return *Off;
       Endif;

       dettrec1 = dettrec;

       Write XTD140R;

       msg_type = type_info;
       msg_text = msgGetText(msg_module:5);

       Return *On;

      /end-free

     P d1AddDettRec    E

      //-----------------------------------------------------------------
      // delete record
      //-----------------------------------------------------------------

     P d1DelDettRec    B                   Export
     D d1DelDettRec    PI              N

      /free

       Delete(e) kls XTD140R;

       If %error;
         msg_type = type_error;
         msg_text = msgGetText(msg_module:6);
         Return *Off;
       Else;
         msg_type = type_info;
         msg_text = msgGetText(msg_module:7);
         Return *On;
       Endif;

      /end-free

     P d1DelDettRec    E

      //-----------------------------------------------------------------
      // validate record
      //-----------------------------------------------------------------

     P d1VfyDettRec    B                   Export
     D d1VfyDettRec    PI              N

      /free

       msg_type = type_error;
       msg_text = msgGetText(msg_module:8);

       If m_APP = *Blanks;
         focusfld = 'APP';
         Return *Off;
       Endif;
       If m_FORM = *Blanks;
         focusfld = 'FORM';
         Return *Off;
       Endif;
       If m_FIELD = *Blanks;
         focusfld = 'FIELD';
         Return *Off;
       Endif;
       If m_TITLE = *Blanks;
         focusfld = 'TITLE';
         Return *Off;
       Endif;

       msg_type = type_info;

       Return *On;

      /end-free

     P d1VfyDettRec    E




----- Original Message ----
From: "Takken, Cor" <cor.takken@xxxxxxxxxxxxx>
To: RPG programming on the AS400 / iSeries <rpg400-l@xxxxxxxxxxxx>
Sent: Wednesday, January 24, 2007 11:35:10 PM
Subject: RE: Organising modules, programs and service programs

Johan,

Externalizing file I/O is in my view the first step towards an
architecture which enables you to use the same database layer (with all
checks, rules and what have you) whether you try to do things via a web
application or 'old fashioned' green screen terminal or enterprise
server bus. A very usefull architecture with a lot of potential.

However nice this is, you will have to do something about the fact that
most oldfashioned programs are written in a fashion which implicitly
takes for granted that the record locks are exclusive are placed and
remain there in the program while it runs (or whenever the lock is
released by another read etc.). If you externalize the file I/O to a
service program, this no longer holds true. Take the example of a
program which reads a record, goes into a subroutine which executes some
stuff and then updates the record which it read before the EXSR. If the
file is 'in the F-spec' this poses no problem, if the file I/O is done
via a service program however there is a big snake under the grass: if
the subroutine calls another program which also accesses this file where
will the file pointer be directed at? Will updates have been done in the
meantime by other processes in the same job? Is there a way to tell?

The move to external file I/O should trigger you to  check the
architecture of all the programs in the (sub)system and an almost
paranoid style of programming needs to be introduced: any update or
delete needs to be preceeded with a read of the file (with lock) and a
comparison of the old record buffer (the one before the fieldvalues were
changed by the program) and the latest read record buffer. Only if the
record contents is equal then the update or delete can go ahead.
Granted: there are currently programs around which have this optimistic
locking mechanism in place, but if this is not the case a considerable
programming effort needs to go in to build this in (and test it
thouroughly).

Moving to SQL for file I/O is also suggested, and indeed SQL also offers
a lot of advantages, however the optimistic locking mechanisms need to
be in place as well. SQL also introduces a new concept of cursors,
result sets, etcetera. Building up a subfile page by page and the
scrolling mechanisms almost force you to link the I/O module to the
program instead of exporting the program as a symbol from a service
program because there is no easy way of transferring the resultset and
the cursor attached between the service program and the using program. I
am not saying the scrolling mechanism and cursor/resultset challenges
compel you to link the I/O module to the program and that it is
absolutely impossible to solve this another way. I am merely saying that
any move away from 'ye olde way' of file I/O methods introduces all
kinds of new issues which need to be addressed.

Don't be surprised if the externalisation forces you to redesign the
programs, and perhaps even forces you to rebuild your entire system. I
am merely trying to warn anybody: The step of externalizing the file I/O
to modules to the program is a step which is often taken much too
lightly. Nonetheless it is an interesting subject and can offer a lot of
advantages, or is even mandatory when moving to a service bus
environment.

My 2 cents,

Cor Takken








 
____________________________________________________________________________________
Don't get soaked.  Take a quick peak at the forecast
with the Yahoo! Search weather shortcut.
http://tools.search.yahoo.com/shortcuts/#loc_weather

As an Amazon Associate we earn from qualifying purchases.

This thread ...


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.