Products

Price/Order

Support

Partners

Testimonials

Test Results

About us

Contact
 Complex Clients - Separating Logic and Preventing Code Duplication
Bottom
 
Total posts: 29
 Author Complex Clients - Separating Logic and Preventing Code Duplication
Kevin Powick

17.12.2007 08:44:29
Registered user
Hi all,

I would have posted this to the Peer Support forum, but there is no posting option at this time.  I'm not sure if this is more of a design question or some lack of knowledge I have about the RTC SDK.  I'm sorry that it's a little long, but I want to give you the full picture.

I've been using the RTC SDK on and off for a while now and really appreciate the flexibility and ease of use, however now I'm running into some issues with larger projects.

For a simple, single-form client application, writing remote functions is pretty easy.  For each remote function, just drop a RtcResult component on your form, RtcxxxClient.Call(RtcResult) your remote function, and then process the Result in the OnReturn event of the RtcResult component.  All very neat.

However, for projects with multiple forms, that may call the same remote functions, it's more complicated.  One approach might be to put a RtcxxxClient, RtcClientModule, and RtcResult compoents on every form -- Then write code for the remote functions specific to each form, and duplicate the code for remote functions that are common to other forms.  Not only would this be silly, but tedious.

Historically, with non-RTC SDK projects, what I've done is to put all "data services" type code into a TDataModule form.  Not only does this keep all the remote communication code in one place, but it separates this code/logic from the rest of the application.  I design it to be a "black box" with methods that return standard objects to the caller. For example, a form dealing with the display of an invoice might retrieve that invoice with something similar to:

procedure TMyForm.DisplayInvoice(InvoiceID: string);
var
  Invoice: TInvoice
begin
   Invoice := MyService.GetInvoice(InvoiceID);
   //... display the invoice
end;

The caller doesn't know, or need to know, how MyService works.  All it "cares" about is that a TInvoice object is returned.  This architecture also allows for easy changes to the communication or database side of things.

The problem with this approach when using RTC is its event-based nature.  If the remote function calls are happening in the "MyService" DataModule, and results are being returned to RtcResult components on that same DataModule, there does not appear to be an easy way to get the information back to the caller IF non-blocking processing is used.

My "solution" to this has been to use RTC in blocking mode via a global TRtcValue variable.

procedure TMyService.rtcResult1Return(Sender: TRtcConnection; Data,
  Result: TRtcValue);
begin;
  gReturnValue.isNull := true;
  gReturnValue.asObject := Result.asObject;
  Result.asObject := nil;
end;

This allows me to set-up my function similar to the following:

function TMyService.GetInvoice(InvoiceID: string):TInvoice
begin

  RtcHttpClient1.Data.newFunction('GetInvoice');
  RtcHttpClient1.Data.asFunction.asString['ID'] := InvoiceID;
  RtcHttpClient1.Call(rtcResult1);
  RtcHttpClient1.WaitForCompletion;

  Result := TInvoice.Create;
  Result.CustID := gReturnValue.asRecord.AsInteger['CustID'];
  Result.Company := gReturnValue.asRecord.AsString['Company'];
  //...
end;

Now, this all works find and dandy, but is there no other way?  There must be someone out there using RTC in complex applications with many forms that would need to call the same remote functions.

--
Kevin Powick

PS. Sorry I don't know how to get code to format correctly in these forums.
Danijel Tkalcec [RTC]

17.12.2007 12:24:20
Registered user
Kevin,

if you would like to continue processing your results through TRtcResult components on multiple DataModules and Forms, while you have the TRtcHttpClient and TRtcClientModule components on a separate datamodule or form, just do it :)

This is how the RTC SDK was designed to be used in non-blocking mode and is the main reason why you can pass another TRtcResult object to every Call() you make. You only need a single TRtcHttpClient and TRtcClientModule component for a RTC client application of any size, with thich you can use any number of TRtcResult components for processing the results.

Since every TRtcResult component has two events, one to be executed in case you receive a result and the other in case of a failure in communicating with the Server, you have full control over what will happen in every specific case from the two events of the TRtcResult component used to issue a remote call.

About the "Peer to Peer" section ...

I have kept the Peer to Peer section closed to non-invited individuals in order to keep the section clean from people who have been posting spam to RTC Forums in every section which I would open up for longer than a few days with "write for all" access. This thought that precaution would help keeping the spammers away, since that way I know exactly who made which post and can block the person should he/she start posting spam or being rude to the community.

I will now open all sections on the Forum to which posting would be allowed after you would register for "Peer to Peer" support (all except direct support, news, faq and announcement sections), so that anyone can post without the need for (free) registration by E-Mail. Should people start posting a spam, or things unrelated to the section they are posting in, I will most likely close the section to the wide public and revert back to using the free registration method (which I have started with).

Best Regards,
Danijel Tkalcec
Johann Campbell

17.12.2007 15:27:35
Registered user
Hi Kevin, the current Remote Function Wizard allows you to create a class container for your functions. You then compile your functions pas file and can use that class in any form that includes it in your 'uses' clause. So you can in essence end up calling your functions like this:

var
  MyInvoice : TrtcDataSet; // if your invoice needs to be represented as a dataset, you can just send it as such
begin
  MyInvoice := MyFunctions.GetInvoice(ID);
  try
    // populate your dataset with the invoice
  finally
    MyInvoice.Free;
  end;
end;


The above method is used when you 'Preserve returned values.' That means that the returned dataset is not destroyed until there is a next function call, so you could use that dataset in another function quickly. Even though I do not use them in other functions, I use the above method mostly, guess it's just habit.

I am currently working on a new remote function builder that I will be sending to Danijel for him to post it to get feedback from RTC users on how best to improve it. I also have a TRtcClientDataSet that has a 'LoadFromRTCDataSet' and 'SaveToRTCDataSet' method that I might post too. It makes dealing with server data so much easier.

Regards,

Johann
Kevin Powick

17.12.2007 15:53:51
Registered user
DT: if you would like to continue processing your results through TRtcResult components on multiple DataModules and Forms, while you have the TRtcHttpClient and TRtcClientModule components on a separate datamodule or form, just do it :)

KP: I understand this, but it is not exactly what I want.  This just means I have 1 TRtcHTTPClient and TRtcClientModule on a single TDataModule form, but I would still require TRtcResult components on multiple forms and, if calling the same remote functions, duplicate code across forms to process Return TRtcResult values.

I want all my RtC components only on one single TDataModule. Other than this single TDatamodule, the rest of my application should known nothing of Rtc.

Imagine I have 10 client-side froms, 1 client-side TDataModule with Rtc and 1 server-side service with Rtc.

[Client TForms x 10] --> [Client TDataModule with Rtc] --> [Server with Rtc]

Those 10 forms should be able to call Functions (not Rtc Remote functions) of TDataModule that return application defined objects. Example:

TDataModule.GetInvoice(InvoiceID:Integer):TInvoice
TDataModule.GetCustomer(CustID:Integer):TCustomer

It will be up to the TDataModule to deal with implementing Rtc Remote functions, parsing the results, and return them to the calling form as a "known" object, such as TInvoice.

The idea is to isolate the "application services" provided by the TDataModule from the rest of my application so that the TDataModule is the only place where data communication and formatting occur.

I hope this makes sense. :)

DT: I have kept the Peer to Peer section closed to non-invited individuals in order to keep the section clean from people who have been posting spam to RTC Forums in every section

KP: Would it not solve the problem to allow anyone that is "registered" on Rtc forums to read/write the Peer To Peer forum?    Since those of us that already have access to the forums based on our current customer status, going through another registration process seems a bit awkward, especially since it is e-mail based.

Various forums I have used allow anyone to register, but when they first register, they can only post to a forum called "Request for Access".  This forum captures all garbage/spam-bot posts, as well as posts from legitimate people requesting access to the rest of the board, which you can grant them.

--
Kevin Powick
Kevin Powick

17.12.2007 16:05:58
Registered user
First off, I wish these forums had an easy way to "quote" another post. :)

JC: Hi Kevin, the current Remote Function Wizard allows you to create a class container for your functions. You then compile your functions pas file and can use that class in any form that includes it in your 'uses' clause. So you can in essence end up calling your functions like this:

var
  MyInvoice : TrtcDataSet; // if your invoice needs to be represented as a dataset, you can just send it as such
begin
  MyInvoice := MyFunctions.GetInvoice(ID);
  try
    // populate your dataset with the invoice
  finally
    MyInvoice.Free;
  end;
end;

Hi Johann,

I have downloaded the wizard and will give it a try.  The "problem" I see in your example is that it uses TrtcDataSet.  I do not want Rtc dependencies sprinkled throughout my application.  I want data related communication and parsing to reside in a single "services" module which other procedures, functions, classes, etc found in various application forms/units can call upon.

--
Kevin Powick
Johann Campbell

17.12.2007 19:04:29
Registered user
That's what the Remote Function Wizard does, it creates a class that hosts your objects. What you consider a service, RTC uses a class. So you would have a class looking like the following:

TMyFunctions = class
  // blah blah blah
public
  function GetMyInvoices(const ID : Integer) : TrtcDataSet;
  function GetMyOrders(const ID : Integer) : TrtcDataSet;
end;


This will be saved in a file that you would add to the uses clause of the datamodules required to have access to these functions. As far as your defined object types such as TInvoice, you will have to create a descendent of TrtcObject. But this will require both server and client to know the interface of those objects. Kind of like the system used in RO. RTC does not require any of that. Instead of using hard-coded properties you use parameters that way, you can on the server side add a property or a field from a database and have the client pick it up without having to write extra code or relying on RTTI.

Regards,

Johann
Kevin Powick

17.12.2007 21:19:18
Registered user
Hi Johann,

I'll give the Remote Functions Wizard a try when I get some more time to look into this.  I do understand what you are saying and how it would be used.

The idea of parameter-based data types, such TRtcDataSet is nice for backwards compatibility of older clients, but in most internal, company applications, we control client side deployment so it's not as critical.  Also, 99% of the time, if you're adding a new field or changing the format on the server side, you want to change the client side anyway to take advantage of it.  I do use RO as well, and know what you mean about making changes that break interfaces.

While not having to rely on the RTTI could be seen as an advantage, it can also be a problem.  When designing new RTC remote functions, one has to constantly refer back and forth between client and server code in order to keep all the parameter names and types straight.  Maybe your wizard helps with this too?  

What I've done to at least take care of the parameter naming issue is to create records of parameter names that goes into a common unit, used by both client and server code.  That way I can take advantage of the IDE's code completion and prevent typos.

Thanks for your feedback,

--
Kevin Powick
Kevin Powick

17.12.2007 22:54:30
Registered user
I've had a chance to play around with the Remote Function Wizard.  It's a nice tool.

However, it really just makes it easier to do what I'm pretty much already doing, using RTC in blocking mode via the "WaitForCompletion" method.

I guess if I want to use the non-blocking, event-based architecture of RTC, I'll have to incorporate TRtcResult components on my client application forms and work with the various RtcValue types.  It's not huge deal.  I was just trying to limit my dependency exposure throughout the application.

--
Kevin Powick
Johann Campbell

18.12.2007 00:02:43
Registered user
Well it would be difficult to implement those functions in a class in non-blocking mode because the result would become available before the result actually returned from the server. However I understand your desire to have a common repository for function results without having to drop RtcResult components on each form. I think this should be solvable if we put ideas together.

Johann
Kevin Powick

18.12.2007 03:22:19
Registered user
Another problem with working in non-blocking, event-driven mode is that it doesn't lend itself well to many common client-side situations of a typical application.  Simplifying the examples I've been giving, let's imagine a single form application, as we see with some of the RTC demos.

On this single form we have 2 TEdit boxes a 2 TLabels

TEdit1...........   Tlable1
TEdit2...........   Tlabel2

Imagine now the idea is to input a known customer number into each of the TEdit boxes, then have the TLabel.caption properties changed to the customer name.

123456  Mr. John Smith
223435  Mr. Bill Brown

As in a real application, each customer name look-up request to the server is triggered by the OnExit event of each TEdit box.

If non-blocking RTC event-driven, I'm going to need 2 rtcResult components to request the exact same customer look-up from the server, because the same RtcResult OnReturn event cannot populate 2 different TLabel captions.  This is the problem with non-blocking design when the same data is required in more than one location in the application, or when processing must occur "in-line", with server data required in the middle of a process.

//processing...
//get data from server
//more processing...

While one might be able to get it to work with an event-driven design, it can become awkward and difficult to follow program flow.

Don't get me wrong.  The ability to work with RTC in non-blocking mode has great advantages for certain types of applications and processing.  It's just that many components of the typical client/server business application are difficult to code this way.  Or at least that what I am finding. :)

I'd be interested to hear how others are dealing with the situations I've described using RTC in non-blocking mode.

--
Kevin Powick
Danijel Tkalcec [RTC]

18.12.2007 03:54:01
Registered user
I think, I have an idea. How about introducing a new Call() method to which you can pass virtually any TObject, then intorducing 2 new events on the TRtcResult component which would pass that TObject as a parameter to the 2 new events?

That would allow you to specify the component to which your data is meant to be sent, in addition to the component responsible for processing the result, which would allow you to use a single TRtcResult component for any number of components in need of the same kind of data.

This is very easy to add to the RTC SDK and it would solve the problem of having to use a separate TRtcResult component when you need the same data to be filled in different places, like it's the case in your example above.

It would be something like this:

You place one TRtcResult component on your DataModule and implement its 2 new events, which will get an additional "Obj:TObject" parameter (or some other name) in the parameter list. When making a remote call, in addition to the TRtcResult component, you could specify any TObject to be passed in to the OnResult event. This would allow you to know what the result was intended for and write allow your event to make some decisions depending on that. For example, check if the TObject parameter was passed and if it is a TLabel, then make the cast and change the label caption based on the result received.

Regards,
Danijel
Johann Campbell

18.12.2007 04:10:33
Registered user
I was thinking something along that line Danijel, but was more focusing on the use of the RtcResult by adding an extra parameter with the component name of the intended result. When RtcResult returns you check the asString['IntendedComponent'] where you could then use your FindComponent techniques and make changes.

I would still discourage the use of regular controls to respond to server data. We should be able to come up with a component that can populate a dataset by responding to RtcResult events. Implementing some callback in TRtcResult that when a specific component that attaches it (TRtcResult) has its callback executed, it can then use the result to update the a connected dataset that will automatically change the Label.

What you think?

Johann
Danijel Tkalcec [RTC]

18.12.2007 04:39:24
Registered user
I am not thinking only about visual components, but any TObject. It would be up to your OnResult event to know what it could or should do with that object.

For example, if you have your own in-memory dataset and want to write your OnResult event to populate this or that dataset with received data, you can do it by passing your dataset object as a parameter to the Call method, in addition to the TRtcResult component. Your local dataset object would not be sent to the Server, it would only be passed to the OnResult event when a result has arrived.

This will allow you to decide for yourself how you want to use that parameter. If you want to place 2 in-memory datasets and 2 data-aware Labels on your form, or only want to use a normal TLabel components which you would directly populate with data received from the Server, would be entirely up to you.

In addition to the TObject parameter, we could also pass an array of variants, so you can specify any additional local parameters required by the result event. Since it could be a mess working with arrays of variants inside events, the Call() method could create a TRtcArray holding all data from the variant array.

A possible parameter list for the new Call() method could be something like ...
1) TRtcResult component
2) TObject
3) array of variants

Any combination of the above parameters is also possible by using overloaded Call() implementations.

Regards,
Danijel
Johann Campbell

18.12.2007 04:55:19
Registered user
I wasn't thinking of passing a dataset to the function. I was thinking of developing a component that bridges the gap between datasets and TRtcResults. This way Kevin could call his functions in response to user actions and have data-aware components respond when a result comes back.

Or even more exotic, have functions that respond to data-aware controls that populate parameters based on field values and actions. Then another component that responds to results. Almost a .NET style scenarion.

Johann
Kevin Powick

18.12.2007 07:00:10
Registered user
One of the great things about the RTC SDK is the stability and scalability of the server side.  This is what a lot of people like about it.  

While some of the ideas you (DT) have just proposed might have some interesting possibilities, I think a lot of people would find it useful if the TRtcXXXClient compoent had a new, blocking method added to it that simply returned a TRtcValue.

Using current RTC style, it would look something like this.

  RtcHTTPServer1.Data.newFunction('ReadOrder');
  RtcHTTPServer1.Data.asFunction.asInteger['OrderID'] := 12345;
  MyRtcValue := RtcHTTPServer1.BlockingCall;

Or perhaps a format with fewer lines of code:

  Params.AsInteger['OrderID'] := 12345
  MyRtcValue := RtcHTTPServer1.BlockingCall('ReadOrder',Params);

"BlockingCall" may not be a good method name. Maybe "SyncCall" for "Synchronous" would be better?

Anyway, by introducing such a method, this would allow the use of BOTH blocking and non-blocking calls within the same program without having to jump through a bunch of hoops, as we have to now to incorporate non-blocking calls for in-line processing.

This would also make it MUCH easier for people to convert existing client-server applications to RTC.

--
Kevin Powick
Danijel Tkalcec [RTC]

18.12.2007 11:20:32
Registered user
Hi Johann,

I think the idea with making data-aware components which would populate themselves with data is quite interesting. Since the RTC SDK uses the approach in which you are passing a TRtcResult component to your call method, you could easily extend any visual or non-visual component by creating an internal TRtcResult object, implement events for populating components data and publish the TRtcResult object as a property which can be used with any Call() method. You could, for example, implement a TRtcLabel which would simply extend a TLabel and publish a Result:TRtcResult property, so you could make calls like ...

RtcClientModule.Data.newFunction('getName').asInteger['ID']:=12345;
RtcClientModule.Call(RtcLabel.RtcResult);

Or ...

RtcClientModule.Data.newFunction('getUserInfo').asInteger['ID']:=12345;
RtcClientModule.Call(RtcClientDataSet.RtcResult);

Or, by making "NewFunction" directly on RtcClientModule ...

RtcClientModule.NewFunction('getUserInfo').asInteger['ID']:=12345;
RtcClientModule.Call(RtcClientDataSet.RtcResult);

Comments?

----

Hi Kevin,

I like the idea with a new "SyncCall" method, but I would make it available also for the standard way of preparing remote calls, so you can easily convert your existing RTC code which is currently using Call and WaitForCompletion to implement blocking calls.

Here is what I could do for the next RTC SDK update ...

I could create a TRtcResult component inside the TRtcClientModule, with OnResult and OnResultAborted events implemented to provide a *real* blocking call mechanism when you set your TRtcHttpClient component's MultiThreaded property to TRUE and use a new BlockingCall method instead of the current Call method to issue your calls. The call would return a pointer to the TRtcValue object received as a result from the server, or raise an exception in the caller thread if there was a problem talking to the server.

This new blocking call implementation will use a semaphore (TRtcEvent) to signal the calling thread when a result has been received or aborted, so the calling thread can wait on the signal instead of looping and cheching some variable every X miliseconds (which would be required for processing socket messages when using RTC SDK in single-threaded non-blocking mode).

To prepare and execute the blocking call using your example function above, you could write something like:

RtcClientModule.Data.newFunction('ReadOrder').asInteger['OrderID']:=12345;
myValue := RtcClientModule.SyncCall;

Or, introducing a "newFunction" to ClientModule to replace "Data.newFunction" ...

RtcClientModule.newFunction('ReadOrder').asInteger['OrderID']:=12345;
myValue := RtcClientModule.SyncCall;

Maybe even add a new "Params" property which would check if a TRtcFunctionInfo object was already created and assigned to Data and return it, or create a new one with FunctionName set to 'Default', in which case you would need to set the name of the function when you make the call ...

RtcClientModule.Params.asInteger['OrderID']:=12345;
myValue := RtcClientModule.SyncCall('ReadOrder');

You could also use the new "Params" property with the non-blocking style, also in a combination with the "NewFunction" method, like ...

RtcClientModule.NewFunction('ReadOrder');
RtcClientModule.Params.asInteger['OrderID']:=12345;
RtcClientModule.SyncCall;

The result would remain available through a new "Result" property, while I would add a new set of "makeCopy" properties which would make use of the current "copyOf" implementation already available on all TRtcValue objects, but would return the object in the same type as the class on which it was called, so you would not have to make a typecase. In other words, a "makeCopy" on a TRtcValue instance will return an identical copy of that TRtcValue, as a TRtcValue (not as TRtcValueObject which you would need to typecase to TRtcValue).

The result object would be kept inside the RtcClientModule component until the next sync call is issued, after which the old data would be destroyed and the result object populated with new data.

And for developers who are comfortable passing parameters in an array without naming each value, I could also introduce a new method for writing single-line remote calls, which could look something like (again using your example from above):

myData := RtcClientModule.Execute('ReadOrder',[12345]);

Or, by using the "Result" object (no need for a local variable) ...

RtcClientModule.Execute('ReadOrder',[12345]);
if RtcClientModule.Result.isType=rtc_DataSet then ...

Comments?

Best Regards,
Danijel Tkalcec
Kevin Powick

18.12.2007 17:01:39
Registered user
Hi Danijel,

I won't comment much on the idea of passing objects (i.e TLabel) to remote functions (OnReturn event).  There may be some situations where it is useful, but I can't see it as a major advantage for the typical applications that I do.

DT> I could create a TRtcResult component inside the TRtcClientModule, with OnResult and OnResultAborted events implemented to provide a *real* blocking call mechanism

Yes, I like this idea a lot.

I think that for developers working with calling remote functions in blocking mode, it is most logical and easiest if the fuction returns a value:

    MyRtcValue := RtcClientModule.SyncCall();
    Edit1.Text := MyRtcValue.asString['FirstName'];

If you want to also store MyRtcValue as a property of RtcClientModule, I suppose that's ok, but I don't want to be "required" to write code like:

    RtcClientModule.Call();
    Edit1.Text := RtcClientModule.Result.asString['FirstName'];
    
I guess it could be handy in some situations to not have to create a local rtcValue variable to deal with the value returned by the function call, but then aren't you are effectively programming with global variables?  I think that leads to bad style and difficult debugging.

You introduced several ideas to prepare and execute the function call.  One was with the introduction of a Params property to RtcClientModule.  I find that one the easiest to read.

    RtcClientModule.Params.asInteger['OrderID']:=12345;
    myRtcValue := RtcClientModule.SyncCall('ReadOrder');

To me, it is clear what is happening.

You have lots of good ideas Danijel.  I think the importance of introducing an easy way to perform blocking calls cannot be understated.  By allowing the developer to use both blocking and non-blocking calls, you give them great flexibility to choose the appropriate calling method for different parts of their applications.

Again, I think it would make it much easier for many people to convert existing applications that use in-line processing logic.  Plus, it would allow them to "free up" non-responsive parts of their applications by changing those sections to non-blocking calls.

--
Kevin Powick
Johann Campbell

18.12.2007 18:36:15
Registered user
But wouldn't the outcome be the same if not require more lines than using Glynn's remote wizard to create a function class?

RtcClientModule.Params.asInteger['InvoiceID'] := 1204;
Result := RtcClientModule.SyncCall('GetInvoiceByID');

as opposed to

Result := ClientFunctions.GetInvoiceByID(1204);


We will be effectively swapping one line of code for 2 lines of code which do the same thing. And the second encapsulates the possible server exceptions that may have arised.

Johann.
Kevin Powick

18.12.2007 20:52:37
Registered user
Hi Johann,

JC> But wouldn't the outcome be the same if not require more lines than using Glynn's remote wizard to create a function class?

KP> Yes, The Remote Functions Wizard (RFW) does provide some of the suggested functionality, but it is not "built-in" to RTC.  

Having this capability as a core part of the RTC SDK would, as Danijel mentioned, implement true blocking within a multi-threaded client as part of its core architecture. It would also ensure that it was available to all users of RTC, without having to download and use separate tools.

It also keeps the component-based nature in-tact for those that like working that way.  Finally, as part of the product itself, it stays within Danijel's control and will be updated as the SDK evolves.

JC> We will be effectively swapping one line of code for 2 lines of code which do the same thing. And the second encapsulates the possible server exceptions that may have arised.

KP> I'm sure exceptions would be considered as part whatever Danijel comes up with if he does indeed decide to add a blocking function call method to RTC.  

In your example for 1 line of code:

    Result := ClientFunctions.GetInvoiceByID(1204);

Danijel already suggested that he could allow and "Execute" method:
    
    Result := RtcClientModule.Execute('GetInvoiceByID',[1024]);

Also, isn't there quite a bit more flexibility in using named parameters than a hard-coded function signature?

I would even like to see the method as:

   Result := RtcClientModule.SynCall(FuncName,Params);

--
Kevin Powick
Johann Campbell

19.12.2007 03:59:12
Registered user
KP - Also, isn't there quite a bit more flexibility in using named parameters than a hard-coded function signature?

Yes there is, however there is the trade off in that there is no well defined interface. You could not or at least would be very radical in developing a shared API using just function names and arrays of parameters. The method you're proposing is good for ad-hoc function calls just as you would find in Data Access Components where you can execute a statement against the server on a whim. However for the design of a well formed API, function signatures IMO are a must.

Not to continuously compare competitors, but almost all remoting frameworks I have come across has in some way compiled source files with signatures of functions. That way, if the function is shared among applications, its interface is not subject to typos. Imagine having your server application open to third party developers, would you prefer to provide a document detailing all function names in string format or a file defining the interface of functions supported by the server?

I would choose the latter. It could just be my familiarity with things like the WinAPI. It's not a - give me the name of a function and its parameters and I give you a result. It's here is the definition of a function and its parameters, execute it and here is its result.

JM2C,

Johann
Kevin Powick

19.12.2007 04:29:10
Registered user
Hi Johann,

I 100% agree with you regarding the API of a real world application and hard-coded function signatures.

In the end, this discussion thread comes full-circle for me, because I would implement my application "Services" in a module that exposes a set of functions (API) to the rest of the application.  This "Services" module could be powered by RTC, or any other remoting framework.

The crux of the matter is that blocking calls that return results directly to the calling function are not currently implemented in RTC.  This can be simulated using the coding techniques we see in the Remote Function Wizard, but I would like to see it built into RTC itself.

Cheers,

--
Kevin Powick
Johann Campbell

20.12.2007 03:49:21
Registered user
Blocking calls as a property or method of an RTC component? Or would you prefer an integrated 'Service' builder?
Kevin Powick

20.12.2007 08:10:53
Registered user
Yes, as part of an RTC component. As Danijel mentioned, TRtcClientModule would work fine.

--
Kevin Powick
Danijel Tkalcec [RTC]

26.12.2007 13:05:04
Registered user
Ok, then ...

I will be adding support for blocking remote function calls in the next RealThinClient SDK update, which should be ready by mid/end of January 2008.

Best Regards,
Danijel Tkalcec
Kevin Powick

23.02.2008 15:56:49
Registered user
Danijel,

Have you implemented this yet?  If not, when?
Danijel Tkalcec [RTC]

23.02.2008 18:39:26
Registered user
Hi Kevin,

No, I haven't implemented this feature in the RTC SDK yet. There have been some unexpected problems with a much higher priority for me, which I need to take care of before I get back to working on new RTC features. From what I can say now is that the next RTC update (RTC SDK and RTC Portal) most likely won't be released before June/July 2008. I hope this will not cause you too much inconvenience.

PS. Since the RTC SDK is now on Sourceforge and accessible through SVN, in case someone is in need of this or some other feature, I'd be happy to give them E-Mail support and write access to the repository so they could extend the components as required, but (unfortunatelly) I won't be able to work on the components for a while.

Best Regards,
Danijel Tkalcec
David Perkins

16.06.2009 15:46:49
Registered user
Hello Danijel

Has this been implemented in the intervening time?

Thanks

David
Danijel Tkalcec [RTC]

16.06.2009 16:20:08
Registered user
Hi David,

no, this hasn't been implemented. But ... if you feel like making your hands dirty and implement this in your local RTC SDK copy and send the files to me for inclusion in the standard RTC SDK distribution once you are comfortable with your newly added functionality, I'd be happy to assist by answering any questions you may have about the RTC SDK (by E-Mail). Everything you need to add this functionality is already there, it would just need to be "wrapped up" in a few easy-to-use methods.

Best Regards,
Danijel Tkalcec
David Perkins

16.06.2009 16:44:29
Registered user
I'm a BCB bod so a patchy knowledge of Pascal and lack of time prevents me from helping out at the moment.