Products

Price/Order

Support

Partners

Testimonials

Test Results

About us

Contact
 RTC Server in a service application
Bottom
 
Total posts: 11
 Author RTC Server in a service application
Ron Levy

29.09.2006 16:26:12
Registered user
Hi,

I need to design an application for automatic product updates - where our application will be able to connect to a server to check for product updates (sort of like Microsoft Windows Update) and purchased an upgrade for RTC for this purpose - so I am now going over the tutorial etc...

A quick question before I decide if I want to work with IIS and an ISAPI extension - is there any reason an RTC server will not be able to work in a Service application? If none - I might just host my server in an SvCom service application.

Thanks,
  Ron.
Danijel Tkalcec [RTC]

29.09.2006 16:41:39
Registered user
No problems that I know of.

It is more likely that some scenarios won't work inside an ISAPI DLL running in a "sandbox" inside IIS. As far as I know, there aren't any problems running RTC Servers as a Windows Service. For example, the RTC WebServer is a mix of a standalone app and a service in one exe and I'm using it as a Service to power the www.realthinclient.com domain (with this forum).

Anyway ... for your application, if you follow the guidelines for separating the application code from the connection component, you will be able to compile the same code into a stand-alone Server, Windows Service and ISAPI extension.

You just need to:
1) Place all your application code (rtcDataProvider, rtcServerModule, rtcFunctionGroup, rtcFunction) in separate TDataModule, without TRtcHttpServer or TRtcISAPIServer. You should link all components on the TDataModule to a TRtcDataServerLink, so you can link them all to a TRtcHttpServer/TRtcISAPIServer later.

2) Create a project for the stand-alone Server, simply starting with a new Delphi Project or a SvCom Service Project - using TRtcHttpServer as your connection component.

3) Create another project to compile your code into an ISAPI extension, by starting with a template ISAPI project from the Lib folder - using TRtcISAPIServer as your connection component.

4) To use the business code implemented in your TDataModule (with TRtcDataProvider and TRtcServerModule components), you just need to link the TRtcDataServerLink from that TDataModule (to which all components like TRtcDataProvider and TRtcServerModule are conencted) to the TRtcHttpServer (in the stand-alone app) or TRtcISAPIServer (in the ISAPI exension).

This way, you have your code separated from the Server components, so you can use it in virtualy *any* RTC Server, simply my linking it to the Server connection component at runtime (before calling Listen).

All RTC Demos are done that way, that's why you will see several different projects using the same units, one compiling the code into a stand-alone Server, the other compiling it into an ISAPI extension.

By folliwing this simple scheme, you can not only use the code to compile it into stand-alone and/or ISAPI extension, but also-use the same code in different project and write your applications as modules which can be integrated into any RTC Server or compiled into one or more ISAPI extensions.

Best Regards,
Danijel Tkalcec
Ron Levy

29.09.2006 23:02:04
Registered user
In my tests so far it seems to be working just fine.
Danijel Tkalcec [RTC]

29.09.2006 23:39:35
Registered user
Yep, the Server and the Service should always be working fine, unless you are doing something in the Service which requires additional Service configuration. For example, you could want to access the Desktop - maybe because you are creating and displaying Windows, or showing an icon in the Tray (in which case you need to make the Service Interactive).

As for the ISAPI, depending on what you want to do, you will need to configure your ISAPI in the Web Server. For example, you could allow only file read access for the ISAPI, or allow full read/write access, or completely disallow all disk access (maybe some company has this policy for security reason?). So, what you can do from an ISAPI mostly depends on how you configure the ISAPI under the Web Server.

Btw ... when you compile your code to run as an ISAPI, it is always multithreaded, unless you set the "ForceSingleThread" property to TRUE. When ForceSingleThreaded property of the TRtcISAPIServer component is True, all requests will be serialized to force single-threaded code execution. This is not recommended (since a long request will block all other requests until it completes sending the response - or connection breaks), but it could be useful if you are not sure how to make your code thread-safe and you are having issues with your ISAPI extension which you can't explain.

Best Regards,
Danijel Tkalcec
Ron Levy

30.09.2006 03:54:08
Registered user
Thanks. I am probably going to have to bother you in the near future about the issue of multi-threading - since I need to access DBIsam from the service application - and in order to do that in a thread safe fashion - I need to create a unique session component for every thread. This is not an issue for me when I have direct control over the threads (for example - my scheduler application has a main thread that looks for jobs that need to be dispatched - and manually creates threads to carry the scheduled jobs when they need to be) - or when I know what instance runs in what thread (I have a MIDAS application in a service for example - and I know that every RDM instance runs in it's own thread) - what I am not sure however is where I "create" my thread specific components.

I would appreciate some pointer to this if possible. Assume that I need to handle a case of a large file upload - my code receives chunks of the data and stores them in a BLOB field. The examples in the SDK make it seem trivial - but my issue is that I might have multiple users doing the same - and I need to have an instance of a specific session component for every thread. (This might be in the documentation - I did not get to it yet and this is an issue I know I will have to tackle - so please excuse me if I am asking a stupid question).
Danijel Tkalcec [RTC]

30.09.2006 10:46:01
Registered user
For database access in a multi-threaded environment, I would recommend creating a database connection pool. It can either be a pool of TDataModule components (where you would place everything you need), or a pool of TDataBase components (with a TSession linked to each). For more information on creating and using a database pool (or generaly a pool of objects), check this FAQ topic:
Database connection pooling

To implement storing a blob into a database, there are several approaches you could take. One which I could recommend is to (1) create an object (extending the TRtcObject class) which will hold the database connection, dataset and the Blob which you will be filling as the data come is, and another approach is to (2) first store the data to a local temporary file as it comes in, then (once you have the whole file) copy it into your BLOB field in a single "run" (in one event) and delete the local file.

1) If you want to use the local temp file approach, you can remember the file name in Request.Info['myfile'] (pick a variable name), so you know where your file is being stored in all your events, then copy everything to a BLOB if 'Request.Completed' by either (1) creating a database connection and everything you need from inside that event, or (2) using a database pool - by taking a database connection (or a TDataModule with all the components you want/need) from the pool, the using that to write the blob.

2) And if you would rather write directly into a BLOB field in your database, without using a temporary local file, you can define your own class by extending the TRtcObject class, where you will store any objects you require for processing th request. You will keep that object in one Request.Info.Obj property, which is directly linked to the Request.

In case the connection should break, "Kill" method will be called on that object, so you can close the stream and return the database connection to the pool (if you are using a database/TDataModule pool) or close the database connection and release the objects (if you have created the database components from your event).

One thing you do NOT need is to glue a database TSession object to a specific thread. Documentation in those Help files is a bit misleading there. The only thing you need is to make sure that one TSession object won't be accessed from multiple threads at the same time and that's what you will ensure by creating those objects from your events or ... a much better approach - by creating a database connection pool where you can keep unused connections and take a connection out when you need it.

Database connection pools are a standard in the "middleware" world. If you want to have efficient access to your database, you need to allow connections to stay open even if they are not used and re-use those connections when needed. Some people go to great lengths to keep the number of active connections as low as possible by adding runtime profiler code which will decide when unused connections can be closed, but I think that's shooting ahead of the target. All you need is a database connection pool which will grow on demand and be able to distinguish valid connections from those that have been dropped by the database (some connection validation is required for all database pools).

Best Regards,
Danijel Tkalcec
Ron Levy

17.10.2006 23:33:09
Registered user
Some update - I finally had the time to start with the application. It seems to be mostly working fine. I am having some problems streaming the results of query datasets in DBIsam - but I suspect that this is an issue with DBIsam - if I find otherwise - I will update you. (I am able to stream TDBIsamTable instances just fine)

An observation - working with remote functions requires an aweful lot of repetitive code to pack function parameters on one side and unpack them on the other. It would be nice if there was an IDE tool that will allow you to define the function signature and create the skeleton code for you. Nothing unworkable - but still - something to consider for a future release.
Glynn Owen [RTC]

18.10.2006 01:02:42
Registered user
Thanks for the suggestion. I've made note of it.
Danijel Tkalcec [RTC]

18.10.2006 06:48:28
Registered user
Ron,

I am not sure what you mean by "pack/unpack function parameters". If you are using the RTC SDK as I thought, you aren't packing the data, you are simply assigning the parameters on one side (client) and using them on the other side (server).

If you are calling the same functions from multiple places in your application and want to be able to call them in a single line, you can write a method (procedure/function) for each of your remote functions, so you can issue remote calls just like local function calls.

To speed-up writing your client-side methods, you can write the procedure declaration inside your class definition (interface part of TDataModule/TForm class) and use the "Complete class" option in Delphi to get the skeleton written by Delphi IDE. Then, you just need to write the code down for preparing and calling the remote functions by using the RTC SDK components. Once you have done that for all your remote functions, you will have an easy way of calling them, without directly using the RTC SDK.

As for the Server-side, I am not sure what you mean by unpacking function parameters, since you get all the parameters exactly as you put them into RTC structures. If you are passing the parameters to other functions/procedures or if you have placed your execution code into a separate class, you simply need to call your functions/procedures by using the parameters you receive in the OnExecute event (as prepared on the Client-side).

If you are working with a database and with repetitive code you mean the packing of dataset data on one side and unpacking it on the other (working with a TDataSet), you can write 2 functions for copying TDataSet data to a RTC dataset on one side and from RTC dataset to a TDataSet on the other side. Take a look at the new example implementation for doing this, which you can find in the "rtcDBTypes" unit of RTC SDK 2.0 RC 15 (and later). This example implementation only handles native types (no blobs), but since you already have experience with packing/unpacking the dataset data, you should be able to use and extend the code to work with DBISAM.

Anyway ... if you have a request for a new feature or tool related to the RTC SDK, please post it to the "Feature Requests" section on the Forum, where we can discuss the issue in more detail. This section is for support issues only.

Best Regards,
Danijel Tkalcec
Ron Levy

18.10.2006 19:09:49
Registered user
Danijel,

Thanks. I am not certain we are on the same page about the observation. I opened a new thread in the Feature Requests section, hopefully the description there is a bit clearer.

Ron.
Danijel Tkalcec [RTC]

18.10.2006 19:59:57
Registered user
Thanks.

Continuing the discussion there ...

Best Regards,
Danijel Tkalcec