Tired of calling the DataSnap server to track down the status of the approval process started earlier? What? What else the topic you thought it will be?
I am taking full advantage of the permission granted to talk about the new features of DataSnap 2010 (DS2010). In this post I will be talking about lightweight callbacks.
Lightweight callbacks can be passed as parameters for the server method based on the new type TDBXCallback delivered with DBXJSON unit. Why there? Because callback can exchange JSON values between server and client. It is the server that is now able to send over data wrapped though a JSON value and waits for a client response. Why lightweight? Because the callbacks ‘live‘ only as long as server method executes.
Your business process can now automatically use client side information to base its decisions on what path to follow next.
TDBXCallback requires one thing only: override its Execute method with the logic you want to be executed when server calls it. It is a class, so you can have fields in it with values specific to each instance. These stateful callbacks are managed by DS2010, i.e. they are freed after server method completes and they are executed within the same thread used to invoke that lengthy server method.
Callbacks on Server Methods
The only requirement a server method needs to fulfill is to have a TDBXCallback type parameter in order to communicate with the client during its execution. In the example below the LoginChatRoom procedure has one callback:
procedure LoginChatRoom(const ChatRoom: string; const UserId: string; callback: TDBXCallback);
A server method can have as many callbacks as it wishes, they can be invoked in any order, but not in parallel since they use the same connection. Callbacks are synchronous, the server resumes execution only after the client responds to it.
Invoking the callback translates in invoking the execute method:
var msg: TJSONObject; response: TJSONValue; ... begin msg := TJSONObject.Create; ... response := callback.Execute(msg); ... response.Free; ... end;
The input parameter is freed by DS2010 but the callback return parameter is owned by the invoker; so it needs to be freed at some moment by the server method.
Invoking Server Methods with Callbacks
Client creates a callback by extending TDBXCallback class, like in the example below:
type TChatCallback = class(TDBXCallback) private FVCLThread: TThread; public constructor Create(vclThread: TThread); function Execute(const Arg: TJSONValue): TJSONValue; override; end;
Usually the server methods with callbacks are lengthy and you may not want the client main thread being stuck while server executes. You also want to localize the callback logic inside the Execute method and avoid complex multi-thread handshake protocols. In the example above the VCL main thread is passed along and the Execute method uses it to update visual components:
function TChatClient.TChatCallback.Execute(const Arg: TJSONValue): TJSONValue; var Data: TJSONValue; begin Data := TJSONValue(Arg.Clone); TThread.Queue(FVCLThread, procedure begin ChatClient.LogMessage(Format('%s - %s: %s', [TJSONObject(data).Get(0).JsonValue.Value, TJSONObject(data).Get(1).JsonValue.Value, TJSONObject(data).Get(2).JsonValue.Value])); Data.Free; end); Result := TJSONObject.Create; TJSONObject(Result).AddPair(TJSONPair.Create('status', TJSONTrue.Create)); end;
The callback queues the argument processing into the VCL thread and ends. The server resumes execution while the UI is updated in parallel.
The simplest way of invoking server methods is through the proxy code. Callback parameters are fully supported by the proxy code generators; the client side methods will have an identical signature as the server methods.
Once the callback class is created an instance can be passed along to the server method:
var conn: TSQLConnection; proxy: TChatServerMethodsClient; ... begin ... conn.Open; proxy := TChatServerMethodsClient.Create(conn.DBXConnection); proxy.LoginChatRoom(ChatClient.ChatRoomEdit.Text, ChatClient.UserIdEdit.Text, TChatCallback.Create(FVCLThread)); ... end;
The callback instance is freed by the DS2010 for you.
Lightweight callbacks are easy to use. They are simple and offer a bi-directional way of communication between server and client. They can also increase the complexity of the code as they introduce new performance challenges, especially on the server side.
For more information please visit Embarcadero RAD Studio 2010.
A chat sample code can be found in Code Central.