Delphi Labs: DataSnap XE - Server Methods Lifecycle

Abstract: In this lab exercise we are going to use Delphi XE to explore different options for DataSnap server methods class instance lifecycle management

Introduction

The key to DataSnap scalability is server methods instances lifecycle management. In the DataSnap architecture clients are remotely invoking public methods of so called “server methods classes”. As a programmer you do not need to worry about creating and destroying server class instances. You only need to tell what the type of a server class is and what its lifecycle policy is. By default server methods lifecycle is “session”, which means that for every connected client there is one dedicated server methods instance that is created when client connects and destroyed when it disconnects. This is quite similar to the concept of a “stateful” Enterprise Java Bean (EJBs). The other possible option for lifecycle is “server”. In this scenario all clients are invoking methods of the same server class instance. Make sure that your implementation of the “server” server methods instance is thread-safe, as its methods might be called from multiple threads simultaneously. The last “lifecycle” option for server method classes is “invocation”. This is probably the most scalable possibility, because server methods class instance is created just for the duration of a method call. It is instantiated just before the request arrives and destroyed when the call is completed. This is quite similar conceptually to “stateless” EJBs.

In this lab exercise we are going to use Delphi XE to build a simple server methods lifecycle demo consisting of server and client applications. First we are going to verify that default scenario where each connected client has its own server methods class instance inside the server. In the second part of the demo we are going to also try two other lifecycle options – “server” and “invocation”.

Now it is a good moment to save the project. Select “File – Save All”. You can save your files wherever you want and give them any names. For all files in this episode I have created “C:\DataSnapLabs\ServerClassesLifecycle” folder. The first file to save is the unit with the application main form so I name it “FormServerUnit”. Keep the default names for server methods classes unit and for the server container unit. I have named the whole project as “LifecycleServer”. Again – this could be anything.

As I am going to have multiple windows on the screen throughout this demo, I’m going to rename the server’s form caption to “Lifecycle Server” and minimize its size slightly as there is not going to be any user interface components on the form. Most server application types, like web apps or console app, do not have any window…

Now let’s quickly add a client application to our server. Right-click in the Project Manager on the top node – which is a project group – and select “Add New Project”. In the “New Items” dialog select Delphi VCL Forms application and click on OK. Save All. Save the main application form as “FormClientUnit”, project as “LifecycleClient” and the project group as just “Lifecycle”.

OK. Let’s return to the server project. At any time there could be only one active project in the IDE. Make sure that the server project is active in the IDE. The easiest way to make a project active is to double-click on its name in the Project Manager. The active project is displayed in bold in the Project Manager and also you can see that its name is displayed at the top of the IDE window.

Implementing Server Methods Class Instance Identification

In the “DataSnap Wizard” we have decided not to add any sample methods to our server class, so it is now completely empty and does not have any members. In the DataSnap architecture defining the client interface is very simple. In the server class implementation any methods that are defined public or published are automatically made available to clients. This is one of the strongest DataSnap features and makes it very dynamic. In order to develop a client in other distributed technologies you typically need a document describing server interface or some other way of obtaining information what are the callable method names, what are the parameter types, names, return values and so on. In DCOM there is a concept of type library, in CORBA there is an IDL and in SOAP Web Services there a WSDL. DataSnap client/server development is conceptually close to generating soap services proxies from running web services application and providing the “\wsdl” path to retrieve the WSDL at runtime. In DataSnap you just need to have access to a running instance of the DataSnap server. That’s it. You retrieve all necessary information at runtime.

During the design of your DataSnap server it is always a good idea to have a test client application and evolve it with the server. In this way you do not have to leave the task of implementing a test client to the very last minute. When the implementation of your server methods change over the time, you just need to regenerate client proxies, so server and client projects are in sync.

For demonstration purposes let’s add to the server methods class implementation a “GetId” public method returning a unique identifier. In this way a client application can check with which server methods class instance inside the server it communicates with.

I have put together a little utility unit that just provides one global function that calls into the underlying OS functionality of generating Globally Unique Identifiers (GUID).

Select “File – New – Unit”. Save the new unit as “GUID_utils” and replace its contents with the following code:

The server methods class contains one private string field called “FID”, an overridden constructor where we initialize the “FID” with a GUID and a public method not taking any arguments and just returning the value stored in FID field.

OK. So how the DataSnap knows that “TServerMethods2” class is our “server method” and its public methods need to be exposed to clients? This is the role of a special “TDSServerClass” component that acts as a link between the main “TDSServer” component and any server methods class. In one DataSnap server application there is always one “TDSServer” component, a number of transport components, through which “TDSServer” communicates with clients and a number of “TDSServerClass” components that provides the “OnGetClass” event where the programmer can assign value to a type of a server methods class that needs to be instantiated when client requests arrive.

The most important part to realize in the implementation of the “OnGetClass” event is the fact that programmer is assigning a type reference and not an instance reference to the parameter. In the DataSnap architecture a programmer is not directly controlling the lifecycle of the server class. There is a “Lifecycle” property on the “TDSServerClass” component that manages the lifetime declaratively. The default value for “DSServerClass1.LifeCycle” property is “Session”.

Now it is time to compile and run server application, because we need a running server instance in order to be able to implement the client.

Build all projects. Make sure that the server project is active in the IDE and click on a green arrow to run the server project without debugging. Minimize the server application window.

Testing “Session” Lifecycle

Double-click on the “LifecycleClient” project in the Project Manager to make it active.

Open the client form and add a button and a label to the form. Probably the easiest way to add components to the form is with the “IDE Insight”. Just press F6 (or “Ctrl+.”) and start typing the name of the component you want to select. Change the button’s caption to “Get ID”.

Add a “TSQLConnection” component to the form.

The “TSQLConnection” is a non-visual component that provides connectivity to DBExpress drivers. The DBExpress framework has been completely re-architected in pure Delphi code in Delphi 2007. In Delphi 2009 the connectivity to DataSnap servers has been implemented in the form of a DBX4 driver that provides connectivity not to an RDBMS server but to a DataSnap server. From the client perspective a DataSnap server instance looks very similar to a database instance and server methods look very much like stored procedures.

Select “TSQLConnection1” component on the form. Modify its “Driver” property to “DataSnap”. Notice that now you can expand “Driver” property in the Object Inspector and configure properties that are specific to DataSnap.

In order to call server methods from a client application, you need to instantiate a generated client class that has the same name as the server class with “Client” appended to it.

What really provides connectivity to the server methods is the “SQLConnection1” component on the form. That is why the client class constructor takes as a parameter a “DBXConnection” parameter, which is a property of “TSQLConnection” class and also the real underlying class implementing the actual connectivity to the server in the underlying component-agnostic layer.

Switch to the main client form and from the “File” menu select “Use Unit” to add newly generated proxy unit to the “uses” clause of the form’s unit.

In order to call a server method you need to instantiate a client class and you can call server methods like if they were a local methods.

Double-click on the button component on the form and implement the following “OnClick event”, which creates a proxy, calls its “GetId” method, displays received value in the label component and then frees the client proxy.

“Save All” and run the client application without debugging by clicking on the green arrow icon.

If you click on the button you should see the unique global identifier of a server methods class instance running inside a remote DataSnap server application.

If you click on the button more times you should see that the identifier does not change.

Now press the green arrow icon in the IDE again to start another instance of a client application. If you click on the button you will see that the identifier is different, which means that both client applications are communicating with different instances of the same server class.

If you try to run the server project at this stage you will get an error saying that the “DSServerMethods2” class has already been added to the server methods list.

This is how the DataSnap architecture has been designed. There is no other way. Different “TDSServerClass” components must return in their “OnGetClass” events references to different classes. You just cannot directly use the same server methods class implementation for multiple “TDSServerClass” components.

To make sure that this demonstration is clear, we do not want any of the server methods class names to stand out.

Go to the server methods class unit and save it as “ServerMethodsUnitSession” and change the name of the server class to “TServerMethodsSession”.

Now we are going to replicate the functionality of the server class just by changing the server class names.

Add to the project a new unit. Name it “ServerMethodsUnitServer”.

Copy and replace the implementation of the server methods class from the “ServerMethodsUnitSession” to “ServerMethodsUnitServer” preserving the unit name.

Add to the project a new unit. Name it “ServerMethodsUnitInvocation”.

Copy and replace the implementation of the server methods class preserving the unit name.

The last task is to properly implement “OnGetClass” events for all three “DSServerClass” components. Add all new units to the server container.

Because we were coping the server class components all three of them point to the same “OnGetClass” implementation. If you want to provide a different event handler just clear the current selection in the “Events” tab of the Object Inspector and double-click to generate a new empty implementation.

The implementation section of the container unit should look like the following:

Now the server is ready. Build all projects and run the server without debugging.

The last thing is to extend the client.

Switch to the client project. Select the “SQLConnection1” component and if its “Connected” property is still set to “true”, set it to “false” and then back to “true” to make sure we are connected with the latest version of the server.

Right click on the “SQLConnection1” component and select “Generate DataSnap Client Classes” from the context menu to regenerate proxy source code. Save modified “proxy” unit. Notice that for every server methods class there is a corresponding client class generated, so now instead of just one client proxy class we have three.

Switch to client application main form. Add two additional buttons and two labels and add code to call different server classes from different buttons and display results in labels.

Summary

In this “Delphi Labs” episode we have looked into different options for DataSnap XE server methods class instances lifecycle.

In this example we have built a test server and a client that communicates over the TCP/IP. Each server methods instance has been assigned a different GUID in its constructor. Using the “GetId” method that returns generated ID, client application could check to which server class instance it is talking to.