In NexusDB communication between Client and Server is facilitated using the Transport components. These Transports implement a bidirectional, multi-thread aware, message exchange mechanism.

On top of this NexusDB implements a low-level RPC framework. This framework is composed of a remote proxy (sometimes referred to as a client stub) in form of TnxRemoteServerEngine and a server stub in form of TnxServerCommandHandler. It is possible to write additional proxies (“Remote Plugins”) and stubs (“Plugin Command Handler”) to build upon that framework.

This low-level RPC implementation has the advantage of having a very low overhead (in runtime performance) which is especially critical for often called procedures which have to transfer a lot of data like RecordGet, RecordInsert and RecordModify. But it also has a number of disadvantages. Adding additional procedures requires a significant amount of coding as the marshalling code in proxy and stub has to be written manually and creating a new plugin is even more involved. The manual static assignment of message numbers opens the possibility for overlapping message numbers between different plugins. Callbacks (Server calling a procedure on the Client as opposed to the normal case of the Client making calls against the Server) while possible, are difficult to implement.

To improve upon this NexusDB 3 will include a high-level, interface based RPC framework which is build on top of the existing RPC implementation. It will be used internally by NexusDB as well as being available to developers creating NexusDB based applications.

I’ll go into more details regarding the capabilities and implementation in future posts. For now, lets have a look at a simple example.

First step is to have server and client agree on a particular interface. As an example we’ll take a simple calculator:

unit CalcIntf;

interface

uses
 nxllTypes,
 nxivTypes;

const
 CLSID_Calc : TnxGuid = '{AA97D66D-F493-4267-95B7-48D166E8B43D}';

type
 InxCalc = interface(InxInvokable)
   ['{5642D468-0B63-4ECF-B932-D33C41932528}']
 
   procedure Clear;

   procedure Add(const aValue: Extended);
   procedure Subtract(const aValue: Extended);
   procedure Multiply(const aValue: Extended);
   procedure Divide(const aValue: Extended);

   function GetResult: Extended;

   property Result: Extended
     read GetResult;
 end;

implementation

initialization
 nxInvokeRegistry.RegisterInterface(TypeInfo(InxCalc));
end.

On the server side we need an actual implementation of this interface.

unit CalcImpl;

interface

uses
 nxrdClass,
 CalcIntf;

type
 TnxCalc = class(TnxClass, InxCalc)
 protected {private}
   clcResult: Extended;
 protected
   {--- InxCalc ---}
   procedure Clear;

   procedure Add(const aValue: Extended);
   procedure Subtract(const aValue: Extended);
   procedure Multiply(const aValue: Extended);
   procedure Divide(const aValue: Extended);

   function GetResult: Extended;
 end;

implementation

uses
 nxrbTypes;

{ TnxCalc }

procedure TnxCalc.Add(const aValue: Extended);
begin
 clcResult := clcResult + aValue;
end;

procedure TnxCalc.Clear;
begin
 clcResult := 0;
end;

procedure TnxCalc.Divide(const aValue: Extended);
begin
 clcResult := clcResult / aValue;
end;

function TnxCalc.GetResult: Extended;
begin
 Result := clcResult;
end;

procedure TnxCalc.Multiply(const aValue: Extended);
begin
 clcResult := clcResult * aValue;
end;

procedure TnxCalc.Subtract(const aValue: Extended);
begin
 clcResult := clcResult - aValue;
end;

var
 Control : InxClassFactoryControl;

initialization
 TnxClassFactory.RegisterClass(CLSID_Calc, TnxCalc, Control);
end.

Once this is in place, getting a interface reference to a newly created instance of this object from the client is very simple:

var
 Calc: InxCalc;
begin
 if RemotingClient.CreateInstance(nil, CLSID_Calc, InxCalc, Calc) <> S_OK then
   exit;
 Calc.Add(1234.56);
 Calc.Multiply(7);
 Calc.Divide(8);
 Calc.Subtract(9);
 WriteLn(Calc.Result);
end;

There is no need to manually write any code for the proxy or stub, no need to define the used interface(s) in any special interface description language or using a type library editor or schema builder or similar software and no need to generate any code before compiling your application.

The above code is all that's required to to define the interface, implement the server and call it from a client.

Home | Community | Blogs | Thorsten's Blog