| 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::<(# 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[];
                           ®ister[]->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;
     #);
#) |