Chapter 21. DataSnap Multitier Connections


Chapter 21. DataSnap Multitier Connections

by Bob Swart


  • Accessing the Server Remotely Using DCOM

  • HTTP WebConnection

  • TCP/IP SocketConnection

  • New DataSnap Connections

  • TSOAPConnection

The previous chapter introduced DataSnap and mainly focussed on the DataSnap Servers and Clients, and this chapter will focus on the communication protocols and different connection components between the DataSnap Servers and Clients.

The examples in this chapter use DataSnap, which means that you must have a copy of the Enterprise Edition of C++Builder to run the programs in this chapter. Note that you can also use the trial version of C++Builder 6 Enterprise to run the examples in this chapter.


Accessing the Server Remotely Using DCOM

In this chapter, we start with the example projects for the SimpleDataSnapServer and DataSnapServer from the previous chapter. They can be found on the CD-ROM with this book.

The objective in this section is to make the connection not work just locally, but also remotely. In other words, DCOM instead of COM (when both the DataSnap Server and Client are running on the same machine).

When setting up DCOM, it is best to set up the server half of the DCOM program on a machine that's running as a Windows NT/2000/XP domain server. In particular, you don't want to run the DataSnap server on a Windows 95/98/Me machine, and it is best if the server machine is a domain server and the client machines are all part of this domain. If you don't have an NT/2000/XP domain server available, you probably should try to set up your client and server machines to have the same logon and the same password, at least during the initial stages of testing. Windows 98/Me ships with DCOM as part of the system, whereas Windows 95 machines need to have DCOM added to the system. You can download the DLLs necessary to implement DCOM on a Windows 95 machine from the Microsoft Web site (although Microsoft officially doesn't support Windows 95 anymore ”nor Windows NT for that matter).

You must have the DataSnap server registered on both the client and the server. The client program could still locate and launch the server if you failed to register it, but COM could not marshal data back and forth if the type library for the server is not registered on the client machine. You can do so by running the DataSnap server once on both machines. However, it's not very convenient to run the DataSnap server on all client machines, so an easier solution is running the DataSnap server once on the server, and then registering the TLB file on the clients using TRegSvr.exe (in the CBuilder6\Bin directory). In this case, the TLB file is called SimpleDataSnapServer.tlb . This file was generated automatically when you created the DataSnap server.

When you access the DataSnap server remotely from a client machine, you need to copy the single C++Builder client executable to the client side only. No database tools are needed, other than the MIDAS.DLL file, which contains the ClientDataSet functionality.


HTTP WebConnection

Apart from using DCOM as a communication protocol, as implemented using the TDCOMConnection component, DataSnap supports two other protocols as well: TCP/IP (sockets) and HTTP. The latter is called TWebConnection and is especially useful in situations where you need to go through a proxy or firewall, which can be quite a problem (or at least quite a task) using the regular TDCOMConnection component.

Before we start using the TWebConnection component in C++Builder 6, I have to warn you that this component does not operate correctly with C++Builder 6, including Update 2. The problem that you'll encounter is an Access Denied message when the DataSnap client tries to connect to the DataSnap server. We have been unable to solve this problem, and can only report that it used to work just fine, and hopefully some forthcoming patch or update from Borland for C++Builder will fix it again in the future.

TWebConnection is found on the DataSnap tab of the Component Palette. It is perfect to use for stateless HTTP communication connecting to a DataSnap server (which is also stateless, as you've seen).

For the remainder of this section, you should probably copy the DataSnapClient project from its original directory (from the previous chapter) to a new directory, where you can experiment on it using the TWebConnection component. Note that this precaution is only necessary with the DataSnapClient project; we won't be modifying the DataSnapServer at all.

Using the DataSnapClient project, you can replace the current TDCOMConnection component with a TWebConnection component. Now the TWebConnection component has to connect to the DataSnapServer you created in this chapter. The TWebConnection component makes this connection by using the HTTP protocol. However, to use a WebConnection , you must make sure that WININET.DLL is installed on the client system (which is available if you have Internet Explorer version 3 or higher installed), the server must have Internet Information Server version 4 or higher or Netscape Enterprise version 3.6 or higher, and finally you must install a special Borland-made ISAPI DLL called HTTPSRVR.DLL (found in the CBuilder6\Bin directory) in a cgi-bin or scripts directory on the Web server that the TWebConnection component uses to connect to. HTTPSRVR is responsible for launching the DataSnapServer on the Web server and will marshal all requests from the client to the application server interface, sending packets of records back.

For more information on Web servers, ISAPI DLLs, and general Web server programming, see Chapter 22, "Web Server Programming with WebSnap."

As a direct consequence, the URL property of the WebConnection component must point to http://localhost/cgi-bin/httpsrvr.dll (which points to the scripts directory on my local machine ”I could also have used Next , you can click the ServerName property, open the list of available DataSnap servers, and select the DataSnapServer.CustomerOrders DataSnap server. You can make sure that the connection actually works by double-clicking the Connected property of the TWebConnection component. If the value turns to true , you're okay.


Note that you don't actually see the DataSnap Web server running. That's because HTTPSRVR (started by the server) is activated by another user (the default Internet user ), and as a result you don't see any visual representation of the middleware server at this time (as you did when using a DCOM or the upcoming Sockets middleware server).

To make sure the server is actually running, you can always take a look at the Task Manager, of course. Inside the Task Manager, you'll see the DataSnapServer running, but (not so) surprisingly, no indication of that is seen at the desktop.

Other than this, the TWebConnection component works exactly the same as the TDCOMConnection component, with one difference ”security. The TWebConnection component enables you to take advantage of SSL security and to communicate with a DataSnapServer application that is protected behind a firewall. For all this, the TWebConnection component has a number of helpful properties such as Proxy , ProxyByPass , UserName , and Password . The UserName and Password properties of TWebConnection can be used to go through a proxy or if the Web server requires authorization or authentication.

Unfortunately, as I wrote in the start of this section, the TWebConnection component will produce an "Access Denied" error when trying to connect a DataSnap Client to the DataSnap Server.

Object Pooling

Finally, a Web connection can use object pooling. This feature enables the server to create a pool of multiple server instances for client requests. This way, the DataSnapServer doesn't use the resource for the remote data module and database connection unless it's actually needed.

Object pooling gives you the ability to set a maximum for the number of instances of the remote data module inside the DataSnap server application. Whenever a client request is received, the DataSnap server checks to see if a free remote data module exists in the pool. If not, it creates a remote data module instance (but never more than the specified maximum number of remote data module instances) or raises an exception with the message Server too busy . The remote data module, in its turn , services the client requests and duly waits for the next one. After a certain period of time without client requests, the remote data module is freed automatically (by the object pooling mechanism).

In previous versions of DataSnap, this feature would not have been possible, because we now have instances of a remote data module that services more than one client. As a result, the server cannot rely on state information ”this has to be maintained by the client. As indicated previously, DataSnap is indeed stateless.

The big question should now be: How do we enable object pooling for HTTP connections? We must get inside the UpdateRegistry() method again ”found in the header file of your remote data module. Inside the UpdateRegistry() method, an object regObj is used to configure the behavior of the application server. With object pooling, we must set three additional property values.

First, regObj.MaxObjects specifies the maximum number of instances. If the DataSnap server receives a client request and no remote data modules are available, an exception with message Server too busy is raised.

Second, regObj.Timeout specifies the number of minutes the remote data module can wait idle in the pool of remote data modules. After spending the specified amount of time without a single client request, the remote data module will be freed automatically by the DataSnap server. According to the documentation, the DataSnap server checks every six minutes to see if any remote data module should be freed. Specifying a timeout value of means that the remote data module will never time out, so in that case the only useful feature you're using is the limit on the amount of remote data module instances.

After these two property settings, the regObj.RegisterPooled must be set to true to indicate that you want to use object pooling.

In practice, there's a fourth property value you can set, regObj.Singleton , which specifies whether the remote data module should be a singleton (but we already set that to false ). If you set it to true , the number of instances and timeout arguments will be ignored, and only a single remote data module (which must be free threaded) will be created to handle all client requests.

An example modified UpdateRegistry for a remote data module with up to 10 instances that time out after 42 minutes of inactivity can be seen in Listing 21.1.

Listing 21.1 UpdateRegistry to enable Connection Pooling
 // Function invoked to (un)register object 
static HRESULT WINAPI UpdateRegistry(BOOL bRegister) 
  TRemoteDataModuleRegistrar regObj(GetObjectCLSID(), 
                                    GetProgID(), GetDescription()); 
  // Disable these flags to disable use by socket or Web connections. 
  // Also set other flags to configure the behavior of your application server. 
  // For more information, see atlmod.h and atlvcl.cpp. 
  regObj.Singleton = false; 
  regObj.MaxObjects = 10; 
  regObj.Timeout = 42; 
  regObj.RegisterPooled = true;  regObj.EnableWeb = true; 
  regObj.EnableSocket = true; 
  return regObj.UpdateRegistry(bRegister); 

Note that I've used hard-coded magic numbers 10 and 42 here. This is not a good idea in real life, especially because it means that you need to recompile the DataSnap server whenever you want to make some changes (for example, if you add new memory to the server, which can then handle more than 10 instances). That's not even considering that the same DataSnap server could be placed on multiple machines, each of a different configuration (see the section "Object Broker," later in this chapter). I always recommend using an external configuration file where you can specify ”for each machine, and for every time you first start the DataSnap server application ”the number of instances and timeout minutes. This adds flexibility to the power already present in object pooling.