5 An example - A Multi-User person register system

Assume an application, where multiple users access one or more person registers concurrently. The registers are stored in one or more persistent stores. This makes a concurrency control mechanism necessary. Moreover, the users could be interested in being informed about important actions performed by the other users. This makes a notification control mechanism necessary.

We will in the following show example implementations of the server and the clients for this application.

5.1 The Server

The server has to provide a lock manager and a notification manager in order to provide concurrency control and notification control. The server has to further bind the PersistentStoreManager, which provides concurrency control, in order to provide also notification control. The implementation of the server, called NPSManager is shown in the following.

5.1.1 NPSManger.bet

ORIGIN '~beta/distribution/shell';
INCLUDE '../serverinterface';     (* the interface to the server *)
INCLUDE '../notificationmanager'; (* to provide a notificationmanager *)

--- program:descriptor ---
shellEnv 
(# 
   shellType:: PersistentStoreManager
     (# 
        thenotificationmanager:^notificationmanager;
     do
        registernotificationmanager:
          (# do
             &notificationmanager[]->thenotificationmanager[]; 
             thenotificationmanager.init;
             (thenotificationmanager[],'Notificationmanager')
                 ->myEnsemble.ns.put(# overWrite:: (# do true -> value #)#);;
          #);
     #);
#)

5.2 The Clients

The clients want to make use of the concurrency control mechanism and the notification mechanism provided by the server. A client opens a shared persistent store and subscribes to UPDATE and WAITFORLOCK notifications for it. It uses the transaction mechanism when accessing a persistent store in order to ensure its consistency. The client can determine, if he wants to read or update the persistent store and he can determine the lock policy to be guaranteed. When being notified, a client displays the notification on the screen. One or more clients can interact concurrently with the server, i.e. several program executions of the client can be executed in parallel. The implementation of a client, called notclient, is shown in the following.

5.2.1 notclient.bet

ORIGIN '~beta/distribution/shell';

INCLUDE 'serverinterface'; (* to communicate with the server *)
INCLUDE 'clientinterface'; (* to bind shelltype to
                              NPSclient (because it uses notifications) *)
INCLUDE 'notification';    (* to subscribe to notifications *)
INCLUDE 'sharedpersistentstore'; (* to make use of the concurrency control mechanism *)
INCLUDE 'recordlib';       (* to store persons in a register *)
INCLUDE 'transaction';     (* to use transactions *)

--- program: descriptor ---
shellEnv
(# 
   shellType:: NPSclient
     (# notifyImpl::(#
                    do theNotification.display;
                    #);
          
        ps: @sharedpersistentstore
          (# maxCountOnDanglerHit::<
               (# do 10->value #);
          #);
        
        serverEnsembleName: ^Text; 
        serverEnsemble: ^ensemble;         
        thenotificationmanager:^notificationmanager;  PSname:^text;
       
        
        newreg:^register;        
        s:^register;
        rootname:^text;
        regname:^text;
        
 do 
        (* Get a reference to the ensemble on which the server is running *)
        (******************************************************************)

        'Enter Persistens Server hostname: ' -> screen.putText;
        keyboard.getLine -> serverEnsembleName[];
                
        getHost: (ensemble##, serverEnsembleName[]) 
          -> theShell.myEnsemble.ns.get -> serverEnsemble[];
        

        (* open shared persistent store *)
        (********************************)

        'Open PS ? '-> screen.puttext;
        keyboard.getline->PSname[];

        open:
          (#  exists:@boolean;                
          do true->exists;
             (PSname[],theShell[],serverensemble[])->PS.open
                     (# notfound::&lt;(# do false->exists; 
                                         'The store is created ...'->puttext; 
                                          PSname[]->PS.create;
                                          leave open;
                                    #); 
                      #);
              (if not exists then 'done'->putline if);
          #);

        
        (* subscribe to UPDATE and WAITFORLOCK notifications *)
        (*****************************************************)
        
        getnotificationmanager:(NotificationManager##, 'Notificationmanager') 
             -> serverEnsemble.ns.get -> thenotificationmanager[];
                
        (if (thenotificationmanager[] <> NONE) then
          subscribe: (# thesubscription: ^subscription;
                       do
                          &subscription[]->thesubscription[];
                          (theShell[],'all',PSname[],Update)
                                ->subscribetoPSEvent->theSubscription[];
                          theSubscription[]
                                ->thenotificationmanager.subscribeNotification;
                          (theShell[],'all',PSname[],Waitforlock)
                                ->subscribetoPSEvent->theSubscription[];
                          theSubscription[]
                                ->thenotificationmanager.subscribeNotification;
                       #);
        if);
        
        (* start transaction loop *)
        (**************************)

        transactionloop:
          (# lmode:@integer; 
             lpolicy:@integer;
          do
             'Access the ps for reading [r] or writing [w]'->putline;
             (if getNonBlank = 'w' then write -> lmode;
             else read -> lmode;
             if);

             'The policy to be guaranteed: MRSW allowed [y,n]'->putline;
             (if getNonBlank = 'y' then shared -> lpolicy;
              else exclusive ->lpolicy;
             if);

             mytrans: ps.transaction
               (# LockMode::<(# do lmode->value#);
                  LockPolicy::<(# do lpolicy->value #);
                  abort::<(# do leave mytrans #);
                  getregister:(# rootname:^text;
                              do
                                 'Which register? '-> screen.puttext; 
                                  keyboard.getline->rootname[];
                                 (rootname[],register##)->get->s[];
                              exit s[]
                              #);   
               do              
                  commandLoop: cycle
                    (# 
                       newreg:^register;
                    do 
                       'Get a register (persistent root) [g]'->putline;
                       'Display register [d]'->putline;
                       'Find person in register [f]'->putline;
                       'Add person to register [a]'->putline;
                       'Delete person from register [k]'->putline;
                       'Change name of person [c]'->putline;
                       
                       'Show registers in location [s]'->putline;
                       'Put register [p]'->putline;
                       'Remove register [r]'->putline;
                       'Checkpoint/Update persistent store [u]'->putline;
                       'Reget in-memory objects [e]'->putline;
                       
                       'Abort the transaction [b]'->putline;
                       'Commit the transaction [q]'->putline;
                       
                       (if getNonBlank
                           
                        // 'g'  then 
                           getregister->s[];
                           (if (not (s[] = NONE)) then
                               'Done'->putline;
                           if);
                           
                        // 'd' then
                           getregister->s[];
                           (if (not (s[] = NONE)) then
                               screen[]->s.display;
                           if);
                           
                        // 'f' then
                           getregister->s[];
                           (if (not (s[] = NONE)) then
                               findWithKey (# do screen[] -> found.display #);
                           if);
                           
                        // 'a' then
                       
                           getregister->s[];
                           (if (not (s[] = NONE)) then
                               addPerson;
                           if);
                                                  
                        // 'k'  then 
                           getregister->s[];
                           (if (not (s[] = NONE)) then
                               delete
                           if);
                           
                       // 'c'  then 
                           getregister->s[];
                           (if (not (s[] = NONE)) then
                             change;
                            if);
                           
                        // 's' then
                           'The following registers are found in the PS:'
                                                              ->putline;
                           scanrootnames (# do current[]->putline; #);
                         
                        // 'p' then
                           'Name of register to be put? '-> screen.puttext; 
                           keyboard.getline->rootname[];
                           &register[]->newreg[];
                           (newreg[],rootname.copy)->put;
                           
                        // 'r' then
                           getregister->s[];
                           (if (not (s[] = NONE)) then
                               NONE->s[];                      
                           if);
                           
                        // 'e' then
                           reget;
                           
                        // 'u' then 
                           checkpoint;
                           
                           
                        // 'b' then 
                           abort;
                                                      
                        // 'q' then 
                           leave commandLoop;
                     if);
                    #);
               #);
             'Continue with new transaction [yn]?'->screen.puttext;
             (if getnonblank = 'y' then
                 restart transactionloop
             if);
             
          #);
        
        (* close shared persistent store *)
        (*********************************)

        PS.close;
        theShell.kill;
     #);
#)


Persistence in BETA - Reference Manual
© 1991-2002 Mjølner Informatics
[Modified: Monday October 23rd 2000 at 10:43]