Forum > Networking and Web Programming
N-tier multiuser application development
JD:
I'm working on the development of a multiuser application using a 3 tier design - a rich client, an application server & a database backend. I have the following options:
a) Build everything in Lazarus
Client: Lazarus Indy application
Application Server: Custom Lazarus Indy application
Database backend: Firebird/PostgreSQL
The client side could use the TMemDataset to send streamed data using Indy TCP components to the application server which uses Zeos to communicate with the database. The problem is TMemDataset cannot handle master/detail relationships unlike the ClientDataset in Delphi. In addition, passing data over sockets can be blocked by firewalls on the client machine & in addition, sensitive data must be encrypted before sending it over the wire. Encrypting & then decrypting data may have a negative impact on application speed.
b) Build a hybrid solution (I)
Client: A rich client based on Lazarus/Delphi
Application Server: Apache Geronimo/Glassfish
Database backend: Firebird/PostgreSQL/JavaDB (also called Apache Derby)
Database server: Apache Server
The client side will use Habari ActiveMQ Client to communicate with the application server. The problem is I've never used it before & I'm wondering about the ease of deployment of this solution. It however looks very promising & is one for the future.
c) Build a hybrid solution (II)
Client: A rich ExtPascal client
Application Server: Custom Lazarus application
Database backend: Firebird/PostgreSQL
Database server: Apache Server
The client could use CGI or FastCGI to communicate with the application server. The problem here again is that I've never tried this before but ExtPascal (based on ExtJS) looks promising & is also likely to be one for the future.
I'm open to suggestions on the best way to approach this problem. I don't want to use a browser based client side because I don't think they are good at handling data displayed in grids. It is absolutely essential for me that the client resembles a normal desktop application but its inner workings make it a web application. I want the users to have a rich desktop experience. :D
I know Lazarus does not have anything like DataSnap or WebSnap, so what can I use as alternatives? I've looked at the excellent FreeSpider component but like I said the grid displays are an issue for me.
I'll greatly appreciate your advice, links, articles etc.
jixian.yang:
Option A is appreciated.
Use Memorytable of Rxlib with source code modified, we can handle the modified, deleted and inserted data, like delta data in delphi.
With SSL support, Synapse and lNet are popular.
JD:
--- Quote from: jixian.yang on August 10, 2010, 06:39:06 pm ---Use Memorytable of Rxlib with source code modified, we can handle the modified, deleted and inserted data, like delta data in delphi.
--- End quote ---
I have RxLib but what does "use Memorytable of RxLib with source code modified..." mean? What source code was modified?
jixian.yang:
The modified code can handle old value and new value of record. If just handle the modified, deleted and inserted data, the override InternalOpen, etc can do, not need to modify source code.
The following is the change:
Comparing files RxMemDsEx.pas and RXMEMDS.PAS
***** RxMemDsEx.pas
unit RxMemDsEx;
{$IFNDEF LINUX}
{$I rxl\rx.inc}
{$ELSE}
{$I RXL/rx.inc}
{$ENDIF}
***** RXMEMDS.PAS
unit rxmemds;
{$I rx.inc}
*****
***** RxMemDsEx.pas
uses SysUtils, Classes, Controls, DB, dbutils;
***** RXMEMDS.PAS
uses SysUtils, Classes, Controls, DB, dbutils;
*****
***** RxMemDsEx.pas
{ TRxMemoryData}
***** RXMEMDS.PAS
{ TRxMemoryData }
*****
***** RxMemDsEx.pas
TRxMemoryData= class(TDataSet)
private
FOldBuf:PChar;
FOnFilterRecordEx: TFilterRecordEvent;
***** RXMEMDS.PAS
TRxMemoryData = class(TDataSet)
private
FOnFilterRecordEx: TFilterRecordEvent;
*****
***** RxMemDsEx.pas
procedure InternalDelete; override;
procedure InternalEdit; override;
procedure InternalPost; override;
***** RXMEMDS.PAS
procedure InternalDelete; override;
procedure InternalPost; override;
*****
***** RxMemDsEx.pas
procedure InternalOpen; override;
procedure InternalCancel; override;
procedure OpenCursor(InfoQuery: Boolean); override;
***** RXMEMDS.PAS
procedure InternalOpen; override;
procedure OpenCursor(InfoQuery: Boolean); override;
*****
***** RxMemDsEx.pas
{ TRxMemoryData}
***** RXMEMDS.PAS
{ TRxMemoryData }
*****
***** RxMemDsEx.pas
FRecords := TList.Create;
FOldBuf := nil;
end;
***** RXMEMDS.PAS
FRecords := TList.Create;
end;
*****
***** RxMemDsEx.pas
procedure TRxMemoryData.InternalEdit;
begin
inherited InternalEdit;
GetMem(FOldBuf,FRecBufSize);
Move(ActiveBuffer^,FOldBuf^,FRecBufSize);
end;
***** RXMEMDS.PAS
{ Records Management }
function TRxMemoryData.GetCapacity: Integer;
begin
if FRecords <> nil then Result := FRecords.Capacity
else Result := 0;
end;
*****
***** RxMemDsEx.pas
{ Records Management }
function TRxMemoryData.GetCapacity: Integer;
begin
if FRecords <> nil then Result := FRecords.Capacity
else Result := 0;
end;
***** RXMEMDS.PAS
procedure TRxMemoryData.SetCapacity(Value: Integer);
begin
if FRecords <> nil then FRecords.Capacity := Value;
end;
*****
***** RxMemDsEx.pas
procedure TRxMemoryData.SetCapacity(Value: Integer);
begin
if FRecords <> nil then FRecords.Capacity := Value;
end;
***** RXMEMDS.PAS
function TRxMemoryData.AddRecord: TMemoryRecord;
begin
Result := TMemoryRecord.Create(Self);
end;
*****
***** RxMemDsEx.pas
function TRxMemoryData.AddRecord: TMemoryRecord;
begin
Result := TMemoryRecord.Create(Self);
end;
function TRxMemoryData.FindRecordID(ID: Integer): TMemoryRecord;
***** RXMEMDS.PAS
function TRxMemoryData.FindRecordID(ID: Integer): TMemoryRecord;
*****
***** RxMemDsEx.pas
dsCalcFields: RecBuf := CalcBuffer;
dsOldValue:
begin
if FOldBuf=nil then
RecBuf:=ActiveBuffer
else
RecBuf:=FOldBuf;
end;
dsFilter: RecBuf := TempBuffer;
***** RXMEMDS.PAS
dsCalcFields: RecBuf := CalcBuffer;
dsFilter: RecBuf := TempBuffer;
*****
***** RxMemDsEx.pas
SetMemoryRecordData(Buffer, Rec.Index);
GetMem(FOldBuf,FRecBufSize);
Move(ActiveBuffer^, FOldBuf^, FRecBufSize);
end;
***** RXMEMDS.PAS
SetMemoryRecordData(Buffer, Rec.Index);
end;
*****
***** RxMemDsEx.pas
end;
FreeMem(FOldBuf);
FOldBuf:=nil;
end;
***** RXMEMDS.PAS
end;
end;
*****
***** RxMemDsEx.pas
// OpenCursor(false);
// 俞疣螯 镱耠?桉镳噔脲龛
{ Fields.Clear;
***** RXMEMDS.PAS
// OpenCursor(false);
// 俞疣螯 镱耠?桉镳噔脲龛
{ Fields.Clear;
*****
***** RxMemDsEx.pas
procedure TRxMemoryData.InternalCancel;
begin
FreeMem(FOldBuf);
FOldBuf:=nil;
inherited InternalCancel;
end;
***** RXMEMDS.PAS
procedure TRxMemoryData.InternalHandleException;
begin
Application.HandleException(Self);
end;
*****
***** RxMemDsEx.pas
procedure TRxMemoryData.InternalHandleException;
begin
Application.HandleException(Self);
end;
***** RXMEMDS.PAS
procedure TRxMemoryData.InternalInitFieldDefs;
begin
end;
*****
***** RxMemDsEx.pas
procedure TRxMemoryData.InternalInitFieldDefs;
begin
end;
***** RXMEMDS.PAS
function TRxMemoryData.IsCursorOpen: Boolean;
begin
Result := FActive;
end;
*****
***** RxMemDsEx.pas
function TRxMemoryData.IsCursorOpen: Boolean;
begin
Result := FActive;
end;
{ Informational }
***** RXMEMDS.PAS
{ Informational }
*****
herux:
Go to Option
D. Using http://wiki.freepascal.org/tiOPF, its support HTTP Proxy-Remote.
Navigation
[0] Message Index
[#] Next page