资源描述
Introdution RemObject SDK
文/黄忠成 EMail:code6421@.tw
前言
DELPHI 的3rd-Party 组件数量之多,远超过其它的开发工具,其用途之广可说创下前所未有的记录。这也为DELPHI 程序设计师省下许多重新制造轮子的时间,令系统开发速度倍增,同时减少了因实作码增加而使错误率升高。可惜的是VCL组件似乎都有着一个通病,就是缺少完整的说明档!许多VCL 组件甚至连范例都少的可怜,幸运的是VCL 组件有个不成文的惯例,那就是多数都会附上完整的原始码,这一点可以稍减其说明档不足的现象。即便如此,说明档不足依旧对使用者造成相当大的困扰,时间就是金钱,在设计者探索原始码时,时间也一点点的流失了。本文所介绍的RemObjects SDK(以下简称RO) 也不能例外,由于这套组件的开发者只有两位,因此说明档一直都是相当短缺,有些地方甚至还有描述错误的情形,但这些缺点却无法掩盖其崭新的创意与高延展性的设计概念,这也是本文为何会出现在读者眼前的主要原因,RO是笔者看过VCL 组件中唯一令笔者感到惊艳的,当然! 这只是笔者个人的感觉,对读者不见得是如此,不过多了解一样东西,于汝何损?? 因此,细细品尝吧!!
PS:本文省略了讨论Web Services的基本知识部份,如果读者对于Web Services不熟悉,可参阅笔者的另几篇文章。
参考文章
电子商务新纪元
电子商务新纪元-失落的章节
Interface Designing
如何取得RO?
读者可至 取得测试版本,正式版本的定价是229 EUR,未来的Enterprise 版本的定价是603.90 EUR,这两个版本都附上了完整的原始码,目前RemObjects Enterprise SDK版本尚在Beta中,此版本拥有许多新功能,除了加强的RO 2.0之外还有抽象化数据存取的Data Abstract组件组,协助除错的Debug Server工具,以及完全使用C# 写成的RO.NET Client SDK。
PS:测试版本仅能运行于DELPHI IDE中,读者可利用Project Group来辅助运行Server端与Client端的程序。
PS2: RO 1.x支持DELPHI 5、6、7 Professional(DataSnap 部份需Enterprise),Kylix 3 for DELPHI。
What’s RemObjects SDK
随着各家厂商的强力背书与推销,Web Services 俨然成为未来分布式系统开发的主流架构,但是Web Services 至今仍然存在一些问题,其中有些是属于规格的问题,有些则是先天上的限制,许多使用Web Services 开发系统的人都会有一个困扰,那就是效率不高,其原因很简单,XML 本身属于纯文字型态,加上必须依赖XML Parser 剖析XML 文件,在传输与解译上都是造成效率不彰的原因,这是Web Services 的先天限制,也是为了兼容性所付出的代价。当然! 如果网络频宽够大,计算机速度够快,这些都不是问题。但事实是目前的频宽与计算机速度还不足以胜任,这使得Web Services 的应用面缩减不少,因此许多的Web Servcies开发工具都会提供将SOAP讯息压缩的解决方案,藉此减少网络传输时间。另一个问题则是Web Services 必须依赖网络通讯协议,以现今的情况来看是以HTTP或TCP两种网络通讯协议为主流,假如客户想将系统安装于一台计算机上(不管是何理由,或许是因为节省金钱),Web Services 还是需要一个占用Port,就实务上来看这并不是什么大问题,但如果可以不占用Port岂不更好?? RO 就是这样一套组件,首先! RO 支持两种讯息标准,一个是SOAP(也就是Web Services)、另一个则是Binary(二进制讯息),支持SOAP 可让其它支持Web Services 的开发工具经由SOAP连上RO Server,支持Binary 可以让RO Client以更快的速度与RO Server 沟通,这比起将SOAP压缩后传递的效率高上许多,更令人兴奋的是RO允许设计者混用这两种讯息协议,也就是说只须撰写一个Server并放上这两个讯息组件,这一个Server 就可以同时服务使用SOAP 与 Binary 讯息的Client 端。有趣吗??更有趣的事情还在后面,RO 支持HTTP、TCP、Windows Message、DLL、UDP(2.0)、MSMQ(RO Enterprise) 多种通讯协议,并且允许设计者混用这些协议(DLL 是例外),简单的说! 就是写一个Server 同时允许Client 端以HTTP、TCP、Windows Message、UDP、MSMQ 方式连结,再加上之前所提的两种讯息标准,这个Server是不是更有趣了呢??呵!还没讲完呢,RO 不但具备这些特色,同时也允许设计者撰写自己的讯息协议与通讯协议,其步骤也不复杂,这些都是RO出色的主要原因。另外RO 也支持Kylix 3 for DELPHI,这代表着使用RO 可撰写Linux Server/Client,Windows Server/Client,日后的RO Client SDK.NET支援.NET Framework、Mono、Ractor,及Compact Framework,你能想象这种情况吗??
PS: TCP 与 Windows Message同时只能支持一种讯息格式,如SOAP 或是 Binary,原因是这两种协议并没有类似URL的概念,HTTP则无此限制,另外RO Enterprise SDK将会支持.NET Binary(.NET Remoting) 与RO Binary 两种格式。
初试RemObjects SDK
谈了这么多空话,现在是时候试试RO的能力了,这一节中以一个简单的计算器为范例(唔!!这是RO 送的,不要都不行….),在安装完RO 后组件盘上会出现RemObjects SDK 页,如下图所示:
其中分为五类,见下表:
组件
功能
类别
TROBinMessagw,TROSOAPMessage
讯息组件,用来处理讯息。
讯息类
TROIndyHTTPServer,
TROIndyTCPServer,
TROBPDXHTTPServer,
TROBPDXTCPServer,
TROWinMessageServer
Server端组件,用来接收讯息,支持HTTP、TCP、Window Message与DLL(DLL 不需要组件,只需export 一个function即可)
Server类
TROIndyHTTPChannel,TROIndyTCPChannel,
TROBPDXHTTPChannel,
TROBPDXTCPChannel,
TROWinInetHTTPChannel,
TRODLLChannel
Client 端组件,用来送出讯息到Server端,支持HTTP,TCP,Windows Message与DLL。
Client类
TRODataSnapConnection,TRODataSnapProviderPublisher
支持DataSnap运作的组件,是的,RemObjects SDK允许使用DataSnap运行于其上。
DataSnap支援
TROWebBrokerServer
Web Broker 支持,允许任何架构于Web Broker之上的网页程序直接挂载RemObjects SDK Server。
Web 支援
表中所提及的组件除BPDX(这是一组Internet 组件,名为DXSock,与Indy 有相同功能,但在效率与稳定性上都比Indy强,不过在易用性上却远不及Indy,而且她属于商业型组件,不过当读者购买RO后不须额外付费就可使用这套组件)、WebBrokerServer、DLLxxx之外,其它都会在本文中运用到。
在对这些组件有一个概略的认识后,现在就可以开始撰写一个简单的程序了,首先请开启New Dialogs 对话盒,切换到RemObjects SDK 这一页,其内有几个Wizard可协助设计者快速的产生骨架程序:
下表是这几个Wizard的简单说明:
Wizard
说明
Apache 2 Shared Module Server Project
建立Apache 2 Shared Module的Server程序。
Apache Shared Module Server Project
建立 Apache 1.x Shared Module的Server程序。
DLL Server Project
建立DLL 的Server程序。
ISAPI/NSAPI Server Project
建立ISAPI、NSAPI 的Server程序。
RemObjects DataSnap Server Module
建立一个支持DataSnap操作的TRODataSnapDataModule。
Windows Executable Server Project
建立一个可独立执行的Server。
表中除了RemObjects DataSnap Server Module之外,其它都是用来建立一个新RO Server项目,这里请选择最简单的Windows Executable Server Project,按下OK后会开启下面这个窗口:
下表是这个窗口的字段说明:
字段
说明
Project Name
专案名称。
Service Library Name
Library 名称,在RO 1.x 中这个参数的用途不大。
Service Name
Web Service 的名称。
Server Class
通讯协议,见Server类组件。
Message Class
讯息协议,见Client 类组件。
Project Directory
项目存放的目录。
输入信息后按下OK就完成了骨架程序的建立工作了,这个程序已包含所有必须
用到的组件,接下来只需启动位于主选单上的RemObjects->Service Builder 工具定义Web Services 的方法即可完成Server端的程序:
RO 预设会帮使用者产生两个方法,一个是Sum、另一个是GetServerTime(不要都不行…),为求简单! 这里直接运用这两个方法,不做任何的变动。请将Service Builder关闭后编译这个项目,此时RO 会跳出一个对话窗询问是否产生Service的定义与实作档案,请选择是,并且在产生后切换到CalcService_Impl.pas加入这两个方法的实作码:
function TCalcService.Sum(const A: Integer; const B: Integer): Integer;
begin
Result:=A+B;
end;
function TCalcService.GetServerTime: DateTime;
begin
Result:=Now;
end;
最后将TROIndyHTTPServer(她被命名为ROServer)的Active设为Ture 就完成了Server端的程序了(假如选择的是BPDX类组件,那么这个动作必须写于程序中,因为BPDX不允许在设计时期启动)。
在撰写Client端程序之前必须先将Server程序执行起来,因为Client端必须由Server取得WSDL(事实上也可以直接由RODL(RO 的定义档)产生呼叫端的程序,后面会介绍这一部份),接着请开立一个新的项目,并且在其FORM 上放入TROWinInetHTTPChannel、TROSOAPMessage 两个组件,然后启动位于主选单上的RemObjects->Import Service 来读入WSDL定义(Import WSDL):
8099是TROIndyHTTPChannel 预设的Port,读者可经由TROIndyHTTPChannel.Port设定可更改其Port地址。
按下Import后会开出Service Builder,其中可以看到CalcService的定义,将Service Builder关闭后RO会要求输入文件名称,请将她命名为CalcService_Intf.pas。最后有个不方便的地方,由于Service Builder 的一个Bug,这个档案必须做一些修改才能正常运作:
//CalcService_EndPointURI = 'http://localhost/soap';
CalcService_EndPointURI = 'http://localhost:8099/soap';
//CalcService_DefaultURN = 'urn-NewLibrary:CalcService';
CalcService_DefaultURN = 'CalcService';
批注部份是原来的程序代码,粗体字部份是修改后的程序代码,这个Bug 已经被确认在下一个版本就会修正。
最后加上一些UI接口在FORM上,并加上呼叫Web Services 的程序代码就完成了Client端程序了,下图是UI 接口:
接着是呼叫Web Services的程序代码:
……………
uses CalcService_Intf;
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:= CoCalcService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:= CoCalcService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
下图是这个程序的执行画面:
很简单是吧?? 唯一美中不足的是那个Bug….
范例程序中附上了BizSnap、.NET、Java 三个Client端的原始码,有兴趣的读者可自行观看,这里就不再说明了。
异步呼叫模式
RO 支持异步呼叫模式,简单的说就是建立一个执行绪来呼叫远程的Web Services,这样在呼叫Web Services的期间主执行绪就可以做其它的事,不会因为长时间的呼叫而导致程序无响应的状况出现。由于目前版本的Service Builder并不会自动产生异步呼叫的Unit,因此使用者必须自行使用命令列的RODL2.EXE 来产生这个Unit,这个工具位于RemObjects SDK\Bin 目录下,只要键入下面这个命令就可产生出这个Unit:
RODL2 /rodl:CalcLibrary.rodl /language:pascal /type:async
CalcLibrary.rodl 是由Service Builder所产生出来的,通常位于Server所在目录下,/language:pascal则是指定产生pascal语言的程序代码(RO未来支持Pascal、C# 两种语言),/type:async则是表示产生出异步呼叫的Unit,完成后会产生一个名为CalcLibrary_Async.pas的程序文件,将它加入Client项目中,并修改呼叫Web Services部份的程序代码:
uses CalcLibrary_Async;
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService_Async;
begin
ROWinInetHTTPChannel1.TargetURL:='http://localhost:8099/soap';
vService:=CoCalcService_Async.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
vService.Invoke_Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text));
Sleep(3000);
lblResult.Caption:=IntToStr(vService.Retrieve_Sum);
finally
vService:=Nil;
end;
end;
由于产生的CalcLibrary_Async.pas有一些小Bug,编译时uses 区段会有错误,请将uses CalcLibrary_Intf 改为CalcService_Intf,这样就可以正常编译并执行了。
多种讯息标准
前面的章节曾经提过,RO 支持混用多种通讯协议与讯息标准,这个功能使得Server的延展性大幅提升,要完成这个功能的步骤相当简单,请开启前一节的Server端专案,并在FORM上放上一个TROBINMessage组件,接着双按TROIndyHTTPServer.Dispatcher属性开出下面这个对话窗:
请按下Add的按纽,接着在Message处选择ROBinMessage1,这样就算完成了讯息标准的设定了,读者也可以藉由更改Path Info属性来变更这个讯息所对应的URL,除此之外,TROBinMessage支持将数据压缩后再传送,这个选项预设是开启的。现在Server已经可以支持SOAP、Binary两种讯息格式了,编译后执行程序后开启Client端的项目,并在FORM上放入一个TROBinMessage与切换使用讯息的RadioGroup组件:
最后修改呼叫端的程序代码:
function TfmMain.GetService:CalcService;
begin
Result:=Nil;
case RadioGroup1.ItemIndex of
0:
begin
ROWinInetHTTPChannel1.TargetURL:='http://localhost:8099/soap';
Result:=CoCalcService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
end;
1:
begin
ROWinInetHTTPChannel1.TargetURL:='http://localhost:8099/bin';
Result:=CoCalcService.Create(ROBINMessage1,ROWinInetHTTPChannel1);
end;
end;
end;
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:=GetService;
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:=GetService;
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
粗体字是变动后的程序代码,主要在于切换不同的讯息标准。完成后执行程序,这时Client已经可以使用SOAP、Binary与Server端沟通了,当然! 两个不一样的Client可以同时使用两种不同的讯息标准与Server沟通。
要了解SOAP 与Binary 的差别,只需使用TCPTrace 这个工具来观察Client与Server的讯息流动状态:
SOAP
BINARY
两种讯息有着近10 倍的差距。
讯息编码
除了支持SOAP与BINARY讯息格式之外,RO 也允许将讯息编码,这对需要较高安全性的Multi-Tier系统来说相当有用,要为系统加入这个功能,只须要设定ROServer的Encryption中的几个属性即可,首先选取欲使用的编码算法:
接着再设定EncryptionRecvKey、EncryptionSendKey就可以了,这两个特性还支持多种算法,这可以防堵有能力的破解高手经由反组译取得加密与解密的Key:
如有需要,也可以将Encryption.UseCompression设为True,这样更增加讯息在传送期间的安全性。
多种通讯协议
上一节中实作了支持多种讯息的Server、Client端,这一节将继续实作支持多种通讯协议的Server端与使用不同通讯协议的Client端。首先开启Server端的项目,在FORM上放入TROIndyTCPServer、TROWinMessageServer,接着设定TROWinMessageServer.ServerID 为”ROCalcServer”,完成后请双按TROIndyTCPServer.Dispatcher与TROWinMessageServer.Dispatcher来设定其支持的讯息组件,由于这两种Server只允许一个讯息组件存在,在本例中选择Binary,最后将这两个组件的Active设成Ture就完成了,下面两个程序是使用TCP与Windows Message的Client端程序代码,完整的程序可于范例目录中找到:
TCPChannel
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROIndyTCPChannel1);
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROIndyTCPChannel1);
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
Window Message Channel
procedure TfmMain.btnCalcClick(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROWinMessageChannel1);
try
lblResult.Caption:=IntToStr(vService.Sum(StrToInt(edtValue1.Text),StrToInt(edtValue2.Text)));
finally
vService:=Nil;
end;
end;
procedure TfmMain.Button1Click(Sender: TObject);
var
vService:CalcService;
begin
vService:=CoCalcService.Create(ROBINMessage1,ROWinMessageChannel1);
try
lblServerTime.Caption:=DateTimeToStr(vService.GetServerTime);
finally
vService:=Nil;
end;
end;
执行画面与之前的程序大同小异,这里就不再贴上来了。
复杂数据型态(Complex Type)
Web Services 除了可以表示简单的数据型态之外,也可以表示复杂的数据型态,这一部份在RO中称之为Struct(结构),这一节中将介绍如何在RO中定义这种数据型态。
其实步骤相当简单,只需在Service Builder中按下Struct按纽新增一个Struct 数据型态,并在其下方定义内部的结构就可以了,如下图:
然后定义一个方法,将其传回值设成这个Struct 即可;
下面是这个方法的实作码:
function TComplexTypeService.GetPerson: Person;
begin
Result:=Person.Create;
Result.Name:='黄忠成';
Result.Age:=18;
end;
Client 端部份只需以Import WSDL 或是Import RODL(选择位于Server项目目录下的那个ComplexTypeService.rodl)即可产生Invoke 部份的程序代码,最后只要加入呼叫Web Services的程序代码就算完成Client端了:
Procedure TfmMain.Button1Click(Sender: TObject);
var
vService:ComplexTypeService;
vData:Person;
begin
vService:=CoComplexTypeService.Create(ROSOAPMessage1,ROWinInetHTTPChannel1);
try
vData:=vService.GetPerson;
try
edtName.Text:=vData.Name;
edtAge.Text:=IntToStr(vData.Age);
finally
vData.Free;
end;
finally
vService:=Nil;
end;
end;
完整的程序请参照范例,除了Struct之外,眼尖的读者应该也看到了RO 支持Enum(列举)、Array(数组)型态,这些型态的使用方式大同小异,日后2.x 会增加Set与Exception的型态定义。
另外有一点必须注意,当Client端是.NET时RO会产生中文乱码的问题,这一点目前笔者与RO Team正在讨论中,下一版本就可解决。
DataSnap 支援
前面曾经提过,RO 允许DataSnap运行于其上,这给了程序设计师一个相当大的礼物,以往DataSnap只能运行于CORBA、DCOM、Socket、SOAP 之上,前面三个通讯协议都属于封闭型的,而SOAP又会遭遇效率低落的问题,使用RO的话不但可以使用HTTP、TCP、Windows Message、DLL,又可以混用SOAP与Binary两种讯息格式,同时具备了延展性与效率,其建构步骤也相当简单,这一节中将撰写一个简单的RO DataSnap Server与Client端。首先请建立一个新项目,并如往常般放置TROIndyHTTPServer、TROSOAPMessage、TROBinMessage至FORM上,接着开启New Dialogs 来加入一个RO DataSnap DataModule:
在其上放置TDatabase、TSession、TQuery、TDataSetProvider等组件:
接着照以往设定Remote DataModule 般设定这几个组件的关联,最后双按DataModule.Providers 加入欲开放给Client端的DataSetProvider 就完成了Server端程序了。
Client端程序也很简单,只需放置需要的UI组件及TROWinInetHTTPChannel、TROBinMessage、TRODataSnapConnection 再做一些设定即可,这部份有一点需注意,由于Indy 的Bug,因此无法在Design Time 混用TRODataSnapConnection与TROIndyHTTPChannel,因此请先利用TROWinInetHTTPChannel,待设定好各ClientDataSet的ProviderName后再将TROWinInetHTTPChannel换成TROIndyHTTPChannel(如果需要的话)。
现在设定TROWinInetHTTPChannel.TargetURL 为http://localhost:8099/bin,并将其Connected设成True,接着切换至TRODataSnapConnection设定Message、Channel这两个特性值分别为TROWinInetHTTPChannel 及 TROBinMesage后将Connect 设成True,现在放置一个TClientDataSet至Form上,并设定其RemoteServer为TRODataSnapConnection,完成后应该就能在ProvideName的下拉盒中找到DataSetProvider1了,最后放置一个DataSource与相关的UI组件就完成了:
一个简单但完整的3-Tier 程序就完成了。
PacketRecords
DataSnap的PacketRecords 一直都是多数人的最爱,RO 也支持这个功能,但事实上这只是RO 众多对象建立模式的一种,下面的章节中会介绍RO 的对象模式与设计概念,这里只需修改DataSnap Server 的Data Module 程序代码就可支持这个功能:
Initialization
{ To adjust scalability of the DataSnapModule and avoid recreation of
instances for each roundtrip, you might want to choose a different
ClassFactory type.
Make sure to check the following url for more information:
}
//TROPooledClassFactory.Create('IAppServer', Create_DataSnapModule, TIAppServer_Invoker, 25);
//TROClassFactory.Create('IAppServer', Create_DataSnapModule, TIAppServer_Invoker);
TROPerClientClassFactory.Create('IAppServer', Create_DataSnapModule, TIAppServer_Invoker, 20*60 { seconds Session timeout });
TROPerClientClassFactory 会为每一个Client建立一个专有的Service 对象,因此运用她就可使PacketRecords 功能正常运作。
DataSnap Server 与 Method
以往在撰写DataSnap Server时常常会定义一些供Client端呼叫的方法,在RO中一样可以这样做,不过因为RO目前版本的一些限制,所以无法很便利的定义这些方法,这点日后会改善(事实上,在RO 2.x Beta中这一点已经解决了)。
RemObjects SDK 的Server 与Client运作模式
相信许多读者一定很好奇,RO 内部到底是如何处理Client端的呼叫,Server又是如何将SOAP转换为呼叫动作执行目的对象中的方法呢?? 事实上,在这一点上RO有非常独到的见解,在详细介绍其运作模式之前,笔者先就目前几种主流的Web Services 开发工具的运作模式做一简单的介绍,其中包括BizSnap、.NET Web Services、.NET Remoting与Java。
BizSnap与.NET Remoting 的Server端运作模式
BizSnap与.NET Remoting有着非常相似的处理模式,甚至可以说除了实作的语言与编译技术不同之外,其主要的概念是完全相同的,下图是其运作模式的流程图:
当Client 将Request 送达Server端后,会经过一个Message Dispatcher机制,这个机制大多是几个重要的组件合作完成,主要在于解出Request中对于所要求对象的描述,以及欲
展开阅读全文