4 GlobalStore Demo examples

In this chapter we'll present some concrete applications which use some functionality provided by this package.

4.1 Faut-tolerant Collaborative drawing application

The application example is a multi-user drawing application. Each user have its own drawing area. A user draws rectangles on the drawing area and moves them with the mouse. The abstraction maintains the coherence of all user's display. The following original centralised program uses the QTk Module. It shows how graphics objects can be controlled on a canvas. We can create rectangles, click on them and drag them with the mouse.

Source File

functor 
import 
   QTk
export 
define 
  Canvas
  Desc=td(canvas( handle:Canvas
                  glue:nswe
                  bg:white))                  
   Window={QTk.build Desc}    
   ListColors= [red green blue yellow]
   Colors={NewCell ListColors}
   proc{CreateRectangle X Y}
     Tag
     Col
   in 
      Tag={Canvas newTag($)}
      case {Access Colors $} of 
         Color|then Col=Color  
      [] nil then {Assign Colors ListColors} Col=red
      end    
      {Assign Colors {List.drop {Access Colors $} 1 $}}
      {Canvas create(rectangle X-50 Y-50 X+50 Y+50 fill:Col tags:Tag)}
      {Bindevent Tag}                
   end 
   proc {Bindevent Tag}      
      {Tag bind(event:"<B1-ButtonRelease>" 
                args:[int(x) int(y)]
                action:proc{$ X Y}
                          {Tag setCoords(X-50 Y-50 X+50 Y+50)}
                       end)}
   end 
   {Canvas bind(event:"<2>" 
                args:[int(x) int(y) ]
               action:CreateRectangle)}  
in   
 {Window show(wait:true)}
end 

To make the drawing program a fault tolerant distributed application, the original centralised program is modified in the following way: First, we create a new global store.

Source File

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Create a new global store
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
functor 
import 
   OS
   Open
   GlobalStore at 'x-ozlib://metwally/dp/GlobalStore.ozf'  
   Application
   Connection
   Pickle
   System(show:Show)
define 
   Pid F
   Ls
   %%% Store emulator pid in a file : user by the test failure script  
   try 
      Pid={OS.getPID}
      F={New Open.file init(name:'/tmp/pidserver' flags:[write create])}       
      {F write(vs:Pid)}
      {F close}
   catch 
      error(_)
   then 
      {Show 'ErrorCreateFile'}
   end    
    
   try 
      {GlobalStore.new ?Ls}
      % pickle LS and offer it to clients
      {Pickle.save {Connection.offerUnlimited Ls $} './gsticket'}
   catch 
      gs(failed_globalstore_creation) then 
      {Show errorNewStore }{Application.exit 0}       
   [] error(url(_ _) debug:_) then {Show 'cannot create url or file '}
   [] error(connection(_ _)  debug:_)  then {Show 'connection Module error'}
   end 
end 

Second, we modify the original centralised code by connecting the new user to the global store and using transactions when we update rectangle objects.

Source File

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Example illustrating the GS
%% Author Almetwally Mostafa
%% Last Modification 27-8-2002
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
functor 
import  
   System(show:Show)
   Module
   Pickle
   Connection
   %Application
export 
define 
   %%% rectangle object class in the Global Store
   class Rectangle 
      prop locking
      attr 
         ox:0
         oy:0
         order
         color
      meth init(X Y Order Color)
         ox <- X
         oy <- Y
         order <- Order
         color <- Color
      end 
      meth getcoord(X Y)
         lock 
            X=@ox
            Y=@oy
         end 
      end 
      meth setcoord(X Y)
         lock 
            ox <- X  
            oy <- Y  
         end 
      end 
      meth getorder(O)
         O=@order
      end 
      meth getcolor(Col)
         Col=@color
      end 
   end 
    
   [QTk]={Module.link ["./QTk.ozf"]}   
   Canvas
   Desc=td(canvas(
                  handle:Canvas
                  glue:nswe
                  bg:white))
                  %tdscrollbar:true
                  %lrscrollbar:true))    
   Window={QTk.build Desc}    
    
 %%%%%%% Dictionary of pairs order store object, store object#graphic object
   Dictobjects={NewDictionary $}
   NewObj LocalStore  Movehere LS
   ListColors= [red green blue yellow]
   Colors={NewCell ListColors}
   proc{UpdateRectangle X Y Obj ?Tag}        
      Tag={Canvas newTag($)}  
      {Canvas create(rectangle X-50 Y-50 X+50 Y+50 fill:{Obj getcolor($)} tags:Tag)}
      {Bindevent Tag Obj}      
   end 
   proc{CreateRectangle X Y}
      Order Sobj       
      Tag
      Col
   in 
      {Show 'Object created '#X#Y}
      Tag={Canvas newTag($)}
      case {Access Colors $} of 
         Color|then Col=Color  
      [] nil then {Assign Colors ListColors} Col=red
      end 
      {Assign Colors {List.drop {Access Colors $} 1 $}}   
      try          
         Sobj={NewObj  Rectangle init(X Y  Order Col)}
         {Show 'Create: Object created'}
         {Show 'Create: Object reference type: '#{Value.type Sobj}}
         {Canvas create(rectangle X-50 Y-50 X+50 Y+50 fill:Col tags:Tag)}
         {Sobj getuniqueobjectid(Order)}    %this is the global reference
         {Dictionary.put Dictobjects Order Sobj#Tag}
         {Bindevent Tag Sobj}          
   catch gs(global_store_temporary_failed) then 
      {Show 'Object not created: retry'}
   end 
   end 
    
   proc {Bindevent Tag  Sobj}
       
      {Tag bind(event:"<B1-ButtonRelease>" 
                args:[int(x) int(y)]
                action:proc{$ X Y}
                          TransRes  
                          proc {Trans Output}
                            {Sobj setcoord(X Y)}
                          end 
                          
                       in 
                          {Tag setCoords(X-50 Y-50 X+50 Y+50)}
                          % initialise transaction on Tag
                          {LocalStore trans(Trans _ TransRes)}  
                          {Wait TransRes}
                       end)}
         %delete this object from store
      {Tag bind(event:"<3>"  
                args:[int(x) int(y)]
                action:proc{$ X Y}
                          Res Order Go
                           
                       in 
                          {Tag setCoords(X-50 Y-50 X+50 Y+50)}
                          {Sobj getuniqueobjectid(Order)}
                          try  
                             {LocalStore deleteobj(Sobj Res)}
                          catch E then {Show E}
                          end 
                          {Wait Res}
                          if Res == commit then 
                             {Dictionary.get Dictobjects Order Sobj#Go}
                             {Go delete}    
                             {Dictionary.remove Dictobjects Order}
                          else 
                             {Show 'Draw: Object NOT deleted !!!'}
                          end 
                       end)}
   end 
    
   {Canvas bind(event:"<2>" 
                args:[int(x) int(y) ]
                action:CreateRectangle)}
    
   
    
in 
   %%%%%%%%%%%%%%%%%%%%%%%%%
   %% Initiate a local store
   %%%%%%%%%%%%%%%%%%%%%%%%%   
   try LS={Connection.take {Pickle.load './gsticket' $}}
   catch 
    error(url(_ _) debug:_) then {Show 'url or file not found'}
   [] error(connection(_ _)  debug:_)  then {Show connectionfailed}
   end 
    
   try 
      {LS newLocal(Module user1  ?NewObj ?LocalStore ?Movehere)}
   catch gs(connectionfailed)
   then 
      {Show connectionfailed}
   end 
   try {Pickle.save {Connection.offerUnlimited LocalStore $} './client1ticket'}
   catch 
    error(url(_ _) debug:_) then {Show 'cannot create url or file '}
   [] error(connection(_ _)  debug:_)  then {Show 'connection Module error'}
   end 
   %%% Save the MOvehere procedure
   try {Pickle.save {Connection.offerUnlimited Movehere $} './movehereticket'}
   catch 
    error(url(_ _) debug:_) then {Show 'cannot create url or file '}
   [] error(connection(_ _)  debug:_)  then {Show 'connection Module error'}
   end  
   % set action when object creation
   {LocalStore setnotifycreation(
      proc {$ O}
         Tag X Y Order in 
         {O getorder(Order)}
         {Wait Order}
         {O getcoord(X Y)}
         {UpdateRectangle X Y O ?Tag}
         {Dictionary.put  Dictobjects Order  O#Tag}
         {Show 'NotifyCreate: Object created'}
         {Show 'NotifyCreate: Object reference type: '#{Value.type O}}
      end)}
    
   % set action when receiving Obj object update
   {LocalStore setnotifyupdate(
    proc {$ ?Obj}
       Tag X Y Order Go   in 
       
       {Obj getorder(Order)}
       {Wait Order}
       {Obj getcoord(X Y)}
       {Dictionary.get Dictobjects Order  Obj#Go}
       {Go delete}  % delete old tage
       {UpdateRectangle X Y Obj ?Tag} % create new tage
       {Dictionary.remove Dictobjects Order} % remove old tage from dictionary
       {Dictionary.put Dictobjects Order Obj#Tag}
      
    end)
   }
    
  
   {LocalStore setnotifydelete(
     proc {$ ?Obj}
        Go  Order  in 
        {Obj getorder(Order)}
        {Dictionary.get Dictobjects Order  Obj#Go}
        {Dictionary.remove Dictobjects Order}
        {Go delete}
     end 
    )
   }
    
   {Window show(wait:true)}
end 
 
 
 
 
 

We can now create another new user connected to GS:

Source File

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Example illustrating the GS
%% Author Almetwally Mostafa
%% Last Modification 27-8-2002
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
functor 
import  
   System(show:Show)
   Module
   Pickle
   Connection
   %Application
export 
define 
   %%% rectangle object class in the Global Store
   class Rectangle 
      prop locking
      attr 
         ox:0
         oy:0
         order
         color
      meth init(X Y Order Color)
         ox <- X
         oy <- Y
         order <- Order
         color <- Color
      end 
      meth getcoord(X Y)
         lock 
            X=@ox
            Y=@oy
         end 
      end 
      meth setcoord(X Y)
         lock 
            ox <- X  
            oy <- Y  
         end 
      end 
      meth getorder(O)
         O=@order
      end 
      meth getcolor(Col)
         Col=@color
      end 
   end 
    
   [QTk]={Module.link ["./QTk.ozf"]}   
   Canvas
   Desc=td(canvas(
                  handle:Canvas
                  glue:nswe
                  bg:white))
                  %tdscrollbar:true
                  %lrscrollbar:true))    
   Window={QTk.build Desc}    
    
 %%%%%%% Dictionary of pairs order store object, store object#graphic object
   Dictobjects={NewDictionary $}
   NewObj LocalStore  Movehere LS
   ListColors= [red green blue yellow]
   Colors={NewCell ListColors}
   proc{UpdateRectangle X Y Obj ?Tag}        
      Tag={Canvas newTag($)}  
      {Canvas create(rectangle X-50 Y-50 X+50 Y+50 fill:{Obj getcolor($)} tags:Tag)}
      {Bindevent Tag Obj}      
   end 
   proc{CreateRectangle X Y}
      Order Sobj       
      Tag
      Col
   in 
      {Show 'Object created '#X#Y}
      Tag={Canvas newTag($)}
      case {Access Colors $} of 
         Color|then Col=Color  
      [] nil then {Assign Colors ListColors} Col=red
      end 
      {Assign Colors {List.drop {Access Colors $} 1 $}}   
      try          
         Sobj={NewObj  Rectangle init(X Y  Order Col)}
         {Show 'Create: Object created'}
         {Show 'Create: Object reference type: '#{Value.type Sobj}}
         {Canvas create(rectangle X-50 Y-50 X+50 Y+50 fill:Col tags:Tag)}
         {Sobj getuniqueobjectid(Order)}    %this is the global reference
         {Dictionary.put Dictobjects Order Sobj#Tag}
         {Bindevent Tag Sobj}          
   catch gs(global_store_temporary_failed) then 
      {Show 'Object not created: retry'}
   end 
   end 
    
   proc {Bindevent Tag  Sobj}
       
      {Tag bind(event:"<B1-ButtonRelease>" 
                args:[int(x) int(y)]
                action:proc{$ X Y}
                          TransRes  
                          proc {Trans Output}
                            {Sobj setcoord(X Y)}
                          end 
                          
                       in 
                          {Tag setCoords(X-50 Y-50 X+50 Y+50)}
                          % initialise transaction on Tag
                          {LocalStore trans(Trans _ TransRes)}  
                          {Wait TransRes}
                       end)}
         %delete this object from store
      {Tag bind(event:"<3>"  
                args:[int(x) int(y)]
                action:proc{$ X Y}
                          Res Order Go
                           
                       in 
                          {Tag setCoords(X-50 Y-50 X+50 Y+50)}
                          {Sobj getuniqueobjectid(Order)}
                          try  
                             {LocalStore deleteobj(Sobj Res)}
                          catch E then {Show E}
                          end 
                          {Wait Res}
                          if Res == commit then 
                             {Dictionary.get Dictobjects Order Sobj#Go}
                             {Go delete}    
                             {Dictionary.remove Dictobjects Order}
                          else 
                             {Show 'Draw: Object NOT deleted !!!'}
                          end 
                       end)}
   end 
    
   {Canvas bind(event:"<2>" 
                args:[int(x) int(y) ]
                action:CreateRectangle)}
    
   
    
in 
   %%%%%%%%%%%%%%%%%%%%%%%%%
   %% Initiate a local store
   %%%%%%%%%%%%%%%%%%%%%%%%%   
   try LS={Connection.take {Pickle.load './gsticket' $}}
   catch 
    error(url(_ _) debug:_) then {Show 'url or file not found'}
   [] error(connection(_ _)  debug:_)  then {Show connectionfailed}
   end 
    
   try 
      {LS newLocal(Module user1  ?NewObj ?LocalStore ?Movehere)}
   catch gs(connectionfailed)
   then 
      {Show connectionfailed}
   end 
   try {Pickle.save {Connection.offerUnlimited LocalStore $} './client1ticket'}
   catch 
    error(url(_ _) debug:_) then {Show 'cannot create url or file '}
   [] error(connection(_ _)  debug:_)  then {Show 'connection Module error'}
   end 
   %%% Save the MOvehere procedure
   try {Pickle.save {Connection.offerUnlimited Movehere $} './movehereticket'}
   catch 
    error(url(_ _) debug:_) then {Show 'cannot create url or file '}
   [] error(connection(_ _)  debug:_)  then {Show 'connection Module error'}
   end  
   % set action when object creation
   {LocalStore setnotifycreation(
      proc {$ O}
         Tag X Y Order in 
         {O getorder(Order)}
         {Wait Order}
         {O getcoord(X Y)}
         {UpdateRectangle X Y O ?Tag}
         {Dictionary.put  Dictobjects Order  O#Tag}
         {Show 'NotifyCreate: Object created'}
         {Show 'NotifyCreate: Object reference type: '#{Value.type O}}
      end)}
    
   % set action when receiving Obj object update
   {LocalStore setnotifyupdate(
    proc {$ ?Obj}
       Tag X Y Order Go   in 
       
       {Obj getorder(Order)}
       {Wait Order}
       {Obj getcoord(X Y)}
       {Dictionary.get Dictobjects Order  Obj#Go}
       {Go delete}  % delete old tage
       {UpdateRectangle X Y Obj ?Tag} % create new tage
       {Dictionary.remove Dictobjects Order} % remove old tage from dictionary
       {Dictionary.put Dictobjects Order Obj#Tag}
      
    end)
   }
    
  
   {LocalStore setnotifydelete(
     proc {$ ?Obj}
        Go  Order  in 
        {Obj getorder(Order)}
        {Dictionary.get Dictobjects Order  Obj#Go}
        {Dictionary.remove Dictobjects Order}
        {Go delete}
     end 
    )
   }
    
   {Window show(wait:true)}
end 
 
 
 
 
 


Ilies Alouini, Mostafa AL-Metwally, Peter Van Roy
Version 1.3.0 (20030218)