RemObjects SDK Services Sample (Delphi)

Getting Started

The RemObjects SDK Services sample demonstrates how to use Hydra to enhance RemObjects SDK services. In this sample we will show how to use Hydra SDK service plugins to dynamically extend server functionality and also use local services.

Please note that you will need the RO SDK for Delphi and the Hydra RO SDK integration package installed in order to build and run this application.

Examine the Server

The server application is a regular RemObjects SDK VCL server application that exposes two simple test services. If you need more information on how to create an RO SDK server, please refer to the RemObjects SDK docs.

Let's examine the most important part of the server:

  TServerForm = class(TForm, ISessionProvider)
    [...]
    ModuleManager: THYModuleManager;
    RODLReader: THYRODLReader;
    [...]
  end;

procedure TServerForm.Button2Click(Sender: TObject);
begin
  with ModuleManager do
    if ModuleCount>0 then
      UnloadModules
    else
      LoadModules('*.dll');
end;

procedure TServerForm.ROServerGetRODLReader(Sender: TROServer;
  var aRODLReader: TROCustomRODLReader);
begin
  aRODLReader := RODLReader;
end;

procedure TServerForm.DisplayServiceCount;
var i, cnt : integer;
begin
  if (csDestroying in ComponentState) then Exit;

  ListBox1.Items.Clear;

  cnt := 0;
  with ModuleManager do
    for i := 0 to (PluginDescriptorCount-1) do
      if (PluginDescriptors[i].PluginType=ptService) then begin
        Inc(cnt);
        ListBox1.Items.Add(PluginDescriptors[i].Name)
      end;

  lbServiceCount.Caption := IntToStr(cnt)+' Dynamic Services Loaded';
end;
  • Button2Click method - Using module manager, we are loading all existing dll's in a server directory or unloading all loaded plugins. This allows us to extend server functionality dynamically in runtime.
  • OnGetRODLReader event handler - This handler assigns Hydra's THYRODLReader component as a custom RODL reader. This component allows our server to merge rodls from plugins into the main rodl.
  • DisplayServiceCount method - This method uses a plugin descriptor to detect the number of service plugins.

Local Services

As mentioned earlier, the server also demonstrates how to use local services. The key component here is the THYLocalService class. This component is used for accessing any local service implemented in either the host application itself or in the Hydra module loaded by the host application.

Let's take a look at how this is done in the sample:

procedure TServerForm.bInvokeLocalServiceClick(Sender: TObject);
var
  mainsvc : IMainService;
begin
  try
    mainsvc := (svcMainService as IMainService);
    ShowMessage('The result is '+IntToStr(mainsvc.Sum(1,2)));
  finally
    if svcMainService.HoldsInstance then
      svcMainService.ReleaseInstance;
  end;
end;

procedure TServerForm.bInvokeHydraServiceClick(Sender: TObject);
var
  hydrasvc : IHydraService;
begin
  try
    hydrasvc := (svcHydraService as IHydraService);
    ShowMessage({$IFDEF UNICODE}AnsiStringToWideString{$ENDIF}(hydrasvc.GetRandomSentence));
  finally
    if svcHydraService.HoldsInstance then
      svcHydraService.ReleaseInstance;
  end;
end;

The first method works with a service that is exposed by the host itself; the second one is trying to access the dynamic service that is located in the Hydra plugin.

As you can see, we are simply casting our svcHydraService and svcMainService, which are THYLocalService components, to the desired service interface and can then use this interface like a regular service.

Also please note that THYLocalService provides the HoldsInstance property, which can show if we already created an instance of the service, as well as the ReleaseInstance method that can release a previously created instance.

Examine the Plugin

Our plugin is a Hydra module that holds an RO SDK service. If you need more information on how to create an RO SDK server, please refer to this article.

Note: We are using a shared package to avoid possible issues with multiple types initialization. If the "_Intf" file is placed in a package that is loaded by the host, they will be initialized only once.

Let's take a look at one of the services:

  THydraService = class(TRORemoteDataModule, IHydraService)
  protected
    function GetRandomSentence: AnsiString;
  end;

[..]

function THydraService.GetRandomSentence: AnsiString;
const Sentences : array[0..3] of Ansistring = (
  'It is a beautiful day',
  'I would love to speak Japanese',
  'I am a RemObjects SDK Service',
  'RemObjects'' stuff is really cool!');
begin
  Randomize;
  result := Sentences[Random(Length(Sentences))];
end;

initialization
  THYROFactory.Create(HInstance,
    TROClassFactory.Create('HydraService', Create_HydraService, THydraService_Invoker));

As you can see, this is a regular RO SDK service, the only difference lies in the registration procedure – it creates a THYROFactory on top of the regular RO factory. Everything else is the same as in server: the services plugins can have multiple services, structures, etc.

Examine the Client

The client is an application that is able to access services exposed by our server. If you need more information on how to create an RO SDK client, please refer to the RemObjects SDK docs..

There is no difference between the server's own services and the dynamic services from a client perspective, so you can work with them as with any other services:

procedure TClientForm.Button1Click(Sender: TObject);
begin
  ShowMessage('The result is '+IntToStr((RORemoteService as IMainService).Sum(1,2)));
end;

procedure TClientForm.Button2Click(Sender: TObject);
begin
  ShowMessage({$IFDEF UNICODE}AnsiStringToWideString{$ENDIF}((svcHydraService as IHydraService).GetRandomSentence));
end;

The only difference occurs when the plugin that contains the service is not loaded; in this case the server will return an exception:

Putting It All Together

Now that we've examined all major parts of the sample, we can build both plugin and host. And after we launch the sample, we should get the following results:

The server is started but the plugin is not loaded. This is the result of the Parse RODL command – as you can see, the server exposes only its own services:

After the plugin is loaded:

Client accessing the dynamic service:

Concepts Covered