Delphi Tuts: DataSnap "Hello World"

By: Pawel Glowacki

Abstract: In this tutorial we are going to use Delphi XE5 to build the simplest possible DataSnap client/server system. The difficulty level of this lab is “Hello World”.

    Create “Hello World” Server Application

The first step is to create a new DataSnap server application using “DataSnap Server” wizard.

Start Delphi XE5.

Select in the main menu “File -> New -> Other”.

Hide image
Pic 00 File New Other

In the “New Items” dialog double-click on the “DataSnap Server” icon in the “Delphi Projects -> DataSnap Server” category.

Hide image
Pic 01 New DataSnap Server

In the first tab keep the default DataSnap “Project type” which is “VCL Forms Application”.

Hide image
Click to see full-sized image

On the second tab keep all the default values. Later we are going to replace sample DataSnap server methods with our own implementation.

Hide image
Click to see full-sized image

On the third screen we keep the default value “211” for the TCP/IP Port. It is always a good idea to click on the “Test Port” to make sure that the selected port is available.

Hide image
Click to see full-sized image

On the last tab we are going to keep default server methods ancestor class which is “TComponent”.

Hide image
Click to see full-sized image

Click on “Finish” and the wizard should create a new project with three units.

Save the whole project by clicking on “File -> Save All”. Alternatively you can click on the “Save All” icon or use “Shift+Ctrl+S” keyboard combination.

Hide image
Pic 06 Save All

Create a new directory for all files in this lab – for example “C:\Labs\Lab01\”.

Save main application form as “FormServerUnit1” and keep default names for all other units – typically “ServerContainerUnit1” and “ServerMethodsUnit1” – and save project as “HelloWorldServer”.

At this stage you should see the following in the Delphi Project Manager:

Hide image
Pic 07 Server in PM

Let’s make sure that all names are meaningful in the server project. This step is optional but making sure that everything has a proper name and caption is a good practice. Double-click on the “FormServerUnit1” form in the Project Manager.

Change the “Caption” property of the form to “Hello World Server”. Resize the form to make it smaller as it is going to be empty. Change the “Name” property of the form to “FormServer”. Save All.

Hide image
Pic 09 Server form at design time

Double-click on the “ServerMethodsUnit1.pas” unit in the Project Manager.

We have chosen in the “DataSnap Server” wizard to add a server method class with sample methods. As this is a really simple demo we are not going to change anything in server methods unit. There are two public methods in the “TServerMethods1” class: “EchoString” and “ReverseString”. Both are taking one string parameter and returning a string value. “EchoString” just returns the input parameter and “ReverseString” reverses the parameter and returns the result. Later on we are going to call the “ReverseString” method from our client application.

The source code of the “ServerMethodsUnit1” is listed below.

unit ServerMethodsUnit1;

interface

uses System.SysUtils, System.Classes, Datasnap.DSServer, Datasnap.DSAuth;

type
{$METHODINFO ON}
  TServerMethods1 = class(TComponent)
  private
    { Private declarations }
  public
    { Public declarations }
    function EchoString(Value: string): string;
    function ReverseString(Value: string): string;
  end;
{$METHODINFO OFF}

implementation

uses System.StrUtils;

function TServerMethods1.EchoString(Value: string): string;
begin
  Result := Value;
end;

function TServerMethods1.ReverseString(Value: string): string;
begin
  Result := System.StrUtils.ReverseString(Value);
end;

end.

Listing 1: ServerMethodsUnit1.pas.

Our server is now fully implemented. In order to develop the client application, the server has to be running.

Click on the “Run Without Debugging” icon to start the server.

Hide image
Pic 10 Run server

Alternatively you could select “Run -> Run Without Debugging” main menu option or press “Shift+Ctrl+F9” keyboard shortcut.

Minimize the server window.

Do not shut down the server until the end of this lab exercise. The very first time that you start the server application, you may receive the Windows Firewall warning that some of the program features are blocked. Make sure that you click on the “Allow access” button to allow the server to communicate with clients.

Hide image
Pic 11 Win Firewall Allow Access

    Create “Hello World” Client Application

Right-click on the project group inside the Project Manager and select “Add New Project”.

Hide image
Pic 12 Add New Project

From the “New Items” dialog select “FireMonkey Desktop Application” from “Delphi Projects” category.

Hide image
Pic 11 New FM Desktop App

Click “OK”.

On the next scree keep “HD FireMonkey Application” as the application type to be created. “HD” stands for “High Definition” and is just a more fancy term for a traditional “2D” graphical user interface. Click “OK”.

Hide image
Pic 12 New HD Form

A new project should be added to the existing project group.

Now click on “Save All” icon.

Browse to the folder where the server project has been saved (“C:\ Labs\Lab01”) and save there the main form unit of the client application as “FormClientUnit1”, the new project as “HelloWorldClient” and the project group as “HelloWorldGrp”.

At this moment the Project Manager should look like this:

Hide image
Pic 15 Prj Grp after adding client app

Our project contains now two projects: the server and the client. At any given time there could be only one “Active” project in the IDE (“Integrated Development Environment”). All commands that you choose in the IDE are applied to the “Active” project. In order to make sure that a given project is “active” you can double-click its name in the Project Manager. The name of the currently active project is displayed in “bold” in Project Manager and you can also see its name as part of the caption of Delphi IDE main window.

Hide image
Pic 16 active project in title bar

Make sure that the “HelloWorldClient” project is active. Click on “File -> New -> Other” and select “DataSnap Client Module” wizard. Click on “OK”.

Hide image
Pic 17 New DataSnap Client Module

Again we are going to keep all the default values in the wizard.

On the first tab keep the “DataSnap server location” to “Local server”.

Hide image
Click to see full-sized image

On the next tab keep the default value “DataSnap stand alone server” and click “Next”.

Hide image
Click to see full-sized image

Our server is using “TCP/IP” as the connection protocol, so keep the default selection on the next screen of the wizard. Click “Next”.

Hide image
Click to see full-sized image

On the last screen click on “Test Connection” to verify that the server listens on the default port 211 and click “Finish”. This is important the connection is OK. Otherwise the wizard would not be able to proceed. It needs access to running server in order to be able to query it for its functionality and generate appropriate source code after we hit “Finish”.

Hide image
Click to see full-sized image

The wizard will add two units to the client project: “ClientClassesUnit1” and “ClientModuleUnit1”. Click on “Save All”.

Open the “ClientClassesUnit1” in the code editor. At the top of the unit you will see a comment that this unit was created by DataSnap proxy generator. In this unit there is “TServerMethods1Client” class, which has among other functions, the “EchoString” and “ReverseString” methods that look like their corresponding methods in the server methods unit in the server project. This is why the server application has to be running why we generate client code using the wizard. The proxy generator checks what the functionality of the running server is and generates appropriate code.

The source code of the “ClientClassesUnit1” is listed below.

unit ClientClassesUnit1;

interface

uses Data.DBXCommon, Data.DBXClient, Data.DBXDataSnap, Data.DBXJSON, Datasnap.DSProxy, System.Classes, System.SysUtils, Data.DB, Data.SqlExpr, Data.DBXDBReaders, Data.DBXCDSReaders, Data.DBXJSONReflect;

type
  TServerMethods1Client = class(TDSAdminClient)
  private
    FEchoStringCommand: TDBXCommand;
    FReverseStringCommand: TDBXCommand;
  public
    constructor Create(ADBXConnection: TDBXConnection); overload;
    constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload;
    destructor Destroy; override;
    function EchoString(Value: string): string;
    function ReverseString(Value: string): string;
  end;

implementation

function TServerMethods1Client.EchoString(Value: string): string;
begin
  if FEchoStringCommand = nil then
  begin
    FEchoStringCommand := FDBXConnection.CreateCommand;
    FEchoStringCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FEchoStringCommand.Text := 'TServerMethods1.EchoString';
    FEchoStringCommand.Prepare;
  end;
  FEchoStringCommand.Parameters[0].Value.SetWideString(Value);
  FEchoStringCommand.ExecuteUpdate;
  Result := FEchoStringCommand.Parameters[1].Value.GetWideString;
end;

function TServerMethods1Client.ReverseString(Value: string): string;
begin
  if FReverseStringCommand = nil then
  begin
    FReverseStringCommand := FDBXConnection.CreateCommand;
    FReverseStringCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FReverseStringCommand.Text := 'TServerMethods1.ReverseString';
    FReverseStringCommand.Prepare;
  end;
  FReverseStringCommand.Parameters[0].Value.SetWideString(Value);
  FReverseStringCommand.ExecuteUpdate;
  Result := FReverseStringCommand.Parameters[1].Value.GetWideString;
end;

constructor TServerMethods1Client.Create(ADBXConnection: TDBXConnection);
begin
  inherited Create(ADBXConnection);
end;

constructor TServerMethods1Client.Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean);
begin
  inherited Create(ADBXConnection, AInstanceOwner);
end;

destructor TServerMethods1Client.Destroy;
begin
  FEchoStringCommand.DisposeOf;
  FReverseStringCommand.DisposeOf;
  inherited;
end;

end.

Listing 2: ClientClassesUnit1.pas.

Now open the “ClientModuleUnit1”. This is a data module. A container for non-visual components. The wizard added a “TSQLConnection” component to the form that has all information about the network address of the server, its communication protocol and port number used for communication. There is also convenient code generated, so the client application can just access the “ServerMethods1Client” property and just start invoking the methods of the “TServerMethod1Client” class that is instantiated on the first access to the property.

The source code of the “ClientModuleUnit1” is listed below.

unit ClientModuleUnit1;

interface

uses
  System.SysUtils, System.Classes, ClientClassesUnit1, Data.DBXDataSnap,
  IPPeerClient, Data.DBXCommon, Data.DB, Data.SqlExpr;

type
  TClientModule1 = class(TDataModule)
    SQLConnection1: TSQLConnection;
  private
    FInstanceOwner: Boolean;
    FServerMethods1Client: TServerMethods1Client;
    function GetServerMethods1Client: TServerMethods1Client;
    { Private declarations }
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property InstanceOwner: Boolean read FInstanceOwner write FInstanceOwner;
    property ServerMethods1Client: TServerMethods1Client read GetServerMethods1Client write FServerMethods1Client;

end;

var
  ClientModule1: TClientModule1;

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}

constructor TClientModule1.Create(AOwner: TComponent);
begin
  inherited;
  FInstanceOwner := True;
end;

destructor TClientModule1.Destroy;
begin
  FServerMethods1Client.Free;
  inherited;
end;

function TClientModule1.GetServerMethods1Client: TServerMethods1Client;
begin
  if FServerMethods1Client = nil then
  begin
    SQLConnection1.Open;
    FServerMethods1Client:= TServerMethods1Client.Create(SQLConnection1.DBXConnection, FInstanceOwner);
  end;
  Result := FServerMethods1Client;
end;

end.

Listing 3: ClientClassesUnit1.pas.

The last task in this lab is to implement the user interface of the client application.

Double-click on the “FormClientUnit1” in the Project Manager to display it.

Change the “Caption” property of the form to “Hello World Client” and its “Name” property to “FormClient”.

Let’s build a simple user interface for our client. We will need an edit box entering the string to be reversed and displaying the result, and a button to invoke the “ReverseString” operation.

In the default layout, at the bottom right part of the IDE there is a Tool Palette with hundreds of reusable components. One way of adding a component to the form is to double-click its icon in the Tool Palette. However if we know what we want to add, probably the fastest way of doing so, is to use “Search” functionality.

In order to set focus on the “Search” box in the IDE you can press “Ctrl” and “.” keys at the same time or just click on the search box at the top of the screen. Now just start typing the name of the desired component. With every key stroke the selection is narrowed down to the component we want. When “TEdit” is selected just press “Enter” to add it. Alternatively we could use the “Search” box in the “Tool Palette” itself.

Hide image
Pic 21 using search to add ctrls

In this way add “TEdit” and “TButton” components to the form. Change the “Text” property of the “Button1” to “Reverse”. We can also add a text to be invoked to the edit box right now, so we do not have to do it when the application will be running. For example change the “Text” property of the “Edit1” component to “Hello World”.

Hide image
Pic 23 Client at design time

The client’s user interface is ready. Now we need to add some code that will be executed when user clicks on the button.

The wizard has generated for us two units with everything that is necessary to connect to our server and invoke its operations. The first step is to make sure that this code is accessible from our client form. For this we need to add “ClientModuleUnit1” to the “uses” clause of the form. We could add this one line of code to the form, but it is faster to use the wizard. Select “File -> Use Unit”.

Hide image
Pic 24 File Use Unit

In the “Use Unit” dialog select “ClientModuleUnit1” and click on “OK”.

Hide image
Pic 23 Add ClientModule unit to uses

Now we are going to write some code that will be executed when the end user click on any of the button. This code will take the contents of the edit box, invoke the “ReverseString” server method and will display the result in the edit.

Double click on the button component. The IDE will move you from the form designer to the code editor, generate an empty “OnClick” event handler for the button and put a cursor in the first line of the event handler where we need to put the code that will be executed when end user will click on the button.

We just need to write one line of code.

uses ClientModuleUnit1;

procedure TFormClient.Button1Click(Sender: TObject);
begin
  Edit1.Text := ClientModule1.ServerMethods1Client.ReverseString(Edit1.Text);
end;

Listing 4: Implementation of the “Button1Click” event handler.

Save All.

Now just run the client application and click on the button. You should see that the contents of the edit box is reversed.

Hide image
Pic 26 Running client

That’s it!

Now you have got the basic skills to use Delphi and create powerful client/server systems with DataSnap!

    Summary

In this tutorial we have used Delphi XE5 for building a simple client/server system with DataSnap technology. We have been using wizards extensively to make our life simpler and build applications faster. Our server has been implemented as a Windows VCL Forms application and client was created as FireMonkey Desktop Application. Client and server were using TCP/IP as the communication protocol to invoke sample “ReverseString” server method.

The difficulty level of this lab was “Hello World” and its goal was to get familiar with using Delphi integrated development environment to work with multiple projects, invoke wizards, use form designer and even write some real Delphi code.

In order to keep this tutorial simple we have only focused on creating a client application for Windows, but we could easily recompile it for Mac OS X.

In the next “Delphi Tuts” step-by-step tutorial we are going to build DataSnap clients for all platforms supported in Delphi XE5: Windows, Mac OS X, iOS and Android!

Server Response from: ETNASC01