Implementing Callbacks
What are Callbacks
With the advent of release 0.9.5 DDObjects does support callbacks. In contrast to remote calls, which are initiated by a client, callbacks are initiated by the server and will be handled by the client. Callbacks offer a mechanism to push data or notify clients. In this tutorial we are going to rebuild the ‘CallbackDemo’ which ships with the release. If you have not read the first tutorial, please do so before proceeding.
Defining the Interface
Again, we do start by defining the interface. Select File|New|Other, select the tabsheet labelled ‘DDObjects’ and doubleclick on the contained icon (DDOWizard) to run the integrated wizard. The name of the class we define should be ‘ClientSession’ and offers three methods:
procedure LogOn(UserName: String; Password: String); procedure LogOff; procedure Notification(DateTime: TDateTime; DataString: String);
The most notable difference, compared to the first tutorial, is that the class is tagged to be ‘Stateful’ and we have selected the procedure Notification as being a ‘Callback’. Stateful does mean, that this object is associated to a specific client. The first time a client invokes a method of that object, a new instance will be created. Subsequent calls will be handled by this instance; it’s exclusevely used by that client.
Again we do execute the sourcecode generation. The wizard will generate five new units of which only two are of interest to us (the remaining three units will be implicitly used). These units define the classes implementing the interface, a wrapper- and a listener class as well as the procedural type which defines the callback.
Creating the Client
Again we do start building the client first. Create a new application and construct the main form’s GUI. Select the tabsheet labelled ‘DDObjects’ within the component-palette and drop a DDORequester on your form. Add the unit uClientSession_Proxy to the project and use it within the form’s code. Note that generated units, which need to be added to the client, always do end with ‘_proxy’. Open the unit uClientSession_Proxy.pas and locate the declaration of TClientSessionProxyStub. Notice the property
property OnNotification: TClientSessionNotification read FClientSessionNotification write FClientSessionNotification;
which is an ordinary property to be associated with an eventhandler. The procedural type can be located within the unit uClientSessionCallbackListener_Stub.pas and does reflect the parameters we did define within the wizard. Any time a callback is received by the server, the eventhandler will be triggered.
Actually there are just a few steps left to finish the client. Firstly we do need to create an instance of the TClientSessionProxyStub and destroy it if we are done. We’ll do this within the OnCreate and OnDestroy handler of the form. Next, we do need to add an eventhandler which does reflect the parameters of TClientSessionNotification and attach it to the instance. Finally, we do need to subscribe at the server in order to receive notifications. This is being done by calling the method Subscribe. The parameter of Subscribe tells the server on which port we are listening for callbacks. In case we do provide an value of 0 the DDOListener component will select an appropiate one of it’s own.
procedure TClientMainForm.FormCreate(Sender: TObject); begin FClientSession := TClientSessionProxyStub.Create(DDORequester1); FClientSession.OnNotification := NotificationReceived; end; procedure TClientMainForm.FormDestroy(Sender: TObject); begin FreeAndNil(FClientSession); end; procedure TClientMainForm.NotificationReceived(Sender: TObject; DateTime: TDateTime; Data: String); begin MemoNotifications.Text := DateTimeToStr(DateTime) + #13#10 + Data; end; procedure TClientMainForm.ButtonSubscribeClick(Sender: TObject); begin FClientSession.Subscribe(0) end;
Creating the Server
Create a new application and construct the main form’s GUI. Once you are done add the unit which has been created by the wizard (uClientSession_Stub.pas) to the project and use it within the form’s code. Select the tabsheet labelled ‘DDObjects’ from the component palette and drop a TDDOListener as well as a TTimer onto your form as shown in the following screenshot:
As within the first tutorial do open the unit uClientSession_Stub.pas, copy the template to the main form’s unit and implement the procedures LogOn and LogOff. The class we inherit from – TClientSession_ActiveStub – already contains the implementation of the procedure Notification which the server will call in order to notify it’s clients.
The code to activate the server and to register the class will be omitted as it’s almost identical to the one already described within the first tutorial.
We still do need to implement the notifications. The listener offers a method called EnumStubs which does take a classreference, a callback as well as an userdefined Integer as it’s parameter (TServerMainForm.Timer1Timer). For each stub, which is of the given type, the callback will be executed along with the concrete stub (TServerMainForm.SendQuote) which we need to cast to TClientSession in order to call the function Notification.
procedure TServerMainForm.SendQuote(Stub: TDDOStubBase; UserData: Integer; var Continue: Boolean); begin Assert(Stub is TClientSession); TClientSession(Stub).Notification(Now, FQuotes[Random(FQuotes.Count)]); end; procedure TServerMainForm.Timer1Timer(Sender: TObject); begin DDOListener1.EnumStubs(TClientSession, SendQuote, 0); end;