Sign in Join
Wiki copytemporary

Web Service Toolkit/pt: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Web Service Toolkit/pt

OldNewLine
1+"Web Service Toolkit” é um pacote de serviços Web para FPC e Lazarus. “Web Service Toolkit” significa facilidade de criação e uso de serviços Web pelos usuários do FPC e do Lazarus.
2+
3+## A Aplicação-Cliente ( uso do serviço )
4+
5+### Visão Geral
6+
7+O “Web Service Toolkit” é feito de duas partes, uma ferramenta de linha de comando “ws_helper” e uma coleção de unidades de suporte. Dado um arquivo de definição de interface (um arquivo WSDL ou um arquivo Pascal descrevendo o serviço Web), “ws_helper” vai criar uma unidade FPC contendo um proxy implementando esta inteface. Em tempo de execução, quando uma chamada ao serviço Web for executada, a ação do proxy é para
8+
9+- Monitoramento dos parâmetros da chamada
10+- fazer a chamada do serviço-alvo
11+- receber o retorno da chamada e desmonitoramento dos parâmetros da saída para o chamador.
12+
13+Atrás da cena, o proxy cuidará dos detalhes do SOAP plumbing.
14+
15+### Exemplo
16+
17+Nós vamos usar o “Amazon E-Commerce Service” disponível livremente (você vai precisar criar uma conta, é free) para uso pessoal neste endereço [1](http://www.amazon.com/gp/browse.html?node=3435361). Para usar este serviço, temos que baixar a interface WSDL (Web Services Description Language) e traduzi-la para a linguagem Pascal. O programa ws_helper manejará isto para nós. Para apresentar suas capacidades, estão abaixo os argumentos de linha de comando que ele suporta.
18+
19+ ws_helper [-uMODE] [-p] [-b] [-i] [-oPATH] inputFilename
20+ -u MODE Generate the pascal translation of the WSDL input file
21+ MODE value may be U for used types or A for all types
22+ -p Generate service proxy
23+ -b Generate service binder
24+ -i Generate service minimal implementation
25+ -o PATH Relative output directory
26+ -a PATH Absolute output directory
27+
28+Para traduzir o arquivoo WSDL baixado do website do “Amazon E-Commerce Service”, execute o seguinte da linha de comando:
29+
30+ ..\..\ws_helper\ws_helper.exe -uA -p -o. AWSECommerceService.wsdl
31+ ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
32+
33+ Parsing the file : AWSECommerceService.wsdl
34+ Interface file generation...
35+ Proxy file generation...
36+ Metadata file generation...
37+ File "AWSECommerceService.wsdl" parsed succesfully.
38+
39+Vão-se produzir dois arquivos:
40+
41+- AWSECommerceService.pas, o arquivo de definição do serviço
42+- AWSECommerceService_proxy.pas, este arquivo contém um proxy que implementa a interface do serviço definida no primeiro arquivo.
43+
44+Abaixo está um extrato dos dois arquivos:
45+
46+``` pascal
47+unit AWSECommerceService;
48+{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
49+interface
50+
51+uses SysUtils, Classes, TypInfo, base_service_intf, service_intf;
52+
53+const
54+ sNAME_SPACE = 'http://webservices.amazon.com/AWSECommerceService/2007-04-04';
55+ sUNIT_NAME = 'AWSECommerceService';
56+
57+type
58+
59+ HelpRequest = class;
60+ Help_RequestArray = class;
61+
62+(...)
63+
64+ AWSECommerceServicePortType = interface(IInvokable)
65+ ['{305A7E48-DD92-4C20-B699-4F2B47C93342}']
66+ function Help(
67+ Const HelpParam : Help_Type
68+ ):HelpResponse_Type;
69+ function ItemSearch(
70+ Const ItemSearchParam : ItemSearch_Type
71+ ):ItemSearchResponse_Type;
72+ function ItemLookup(
73+ Const ItemLookupParam : ItemLookup_Type
74+ ):ItemLookupResponse_Type;
75+ (...)
76+ end;
77+
78+ procedure Register_AWSECommerceService_ServiceMetadata();
79+```
80+
81+``` pascal
82+Unit AWSECommerceService_proxy;
83+{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
84+Interface
85+
86+Uses SysUtils, Classes, TypInfo, base_service_intf, service_intf, AWSECommerceService;
87+
88+Type
89+
90+ TAWSECommerceServicePortType_Proxy=class(TBaseProxy,AWSECommerceServicePortType)
91+ Protected
92+ class function GetServiceType() : PTypeInfo;override;
93+ function Help(
94+ Const HelpParam : Help_Type
95+ ):HelpResponse_Type;
96+ function ItemSearch(
97+ Const ItemSearchParam : ItemSearch_Type
98+ ):ItemSearchResponse_Type;
99+ function ItemLookup(
100+ Const ItemLookupParam : ItemLookup_Type
101+ ):ItemLookupResponse_Type;
102+ (...)
103+ End;
104+
105+ Function wst_CreateInstance_AWSECommerceServicePortType(
106+ const AFormat : string = 'SOAP:';
107+ const ATransport : string = 'HTTP:'
108+ ):AWSECommerceServicePortType;
109+
110+Implementation
111+uses wst_resources_imp, metadata_repository;
112+
113+Function wst_CreateInstance_AWSECommerceServicePortType(
114+ const AFormat : string;
115+ const ATransport : string
116+):AWSECommerceServicePortType;
117+Begin
118+ Result := TAWSECommerceServicePortType_Proxy.Create(
119+ 'AWSECommerceServicePortType',
120+ AFormat +
121+ GetServiceDefaultFormatProperties(TypeInfo(AWSECommerceServicePortType)),
122+ ATransport + 'address=' + GetServiceDefaultAddress(TypeInfo(AWSECommerceServicePortType))
123+ );
124+End;
125+
126+function TAWSECommerceServicePortType_Proxy.ItemSearch(
127+ Const ItemSearchParam : ItemSearch_Type
128+):ItemSearchResponse_Type;
129+Var
130+ locSerializer : IFormatterClient;
131+ strPrmName : string;
132+Begin
133+ locSerializer := GetSerializer();
134+ Try
135+ locSerializer.BeginCall('ItemSearch', GetTarget(),(Self as ICallContext));
136+ locSerializer.Put('ItemSearch', TypeInfo(ItemSearch_Type), ItemSearchParam);
137+ locSerializer.EndCall();
138+
139+ MakeCall();
140+
141+ locSerializer.BeginCallRead((Self as ICallContext));
142+ TObject(Result) := Nil;
143+ strPrmName := 'ItemSearchResponse';
144+ locSerializer.Get(TypeInfo(ItemSearchResponse_Type), strPrmName, Result);
145+
146+ Finally
147+ locSerializer.Clear();
148+ End;
149+End;
150+(...)
151+```
152+
153+Nós agora podemos construir um programa simples para o serviço. A biblioteca Synapse é requerida para compilar o programa como ele é usado para a comunicação HTTP. Esta biblioteca pode ser baixada livre de taxa neste endereço: [2](http://synapse.ararat.cz/). A biblioteca Indy [3](http://www.indyproject.org/Sockets/fpc/index.en.html) ou a biblioteca ICS [4](http://www.overbyte.be/frame_index.html) também podem ser usadas.
154+
155+``` pascal
156+program amazon_sample;
157+{$mode objfpc}{$H+}
158+uses
159+ Classes, SysUtils, soap_formatter, synapse_http_protocol,
160+ metadata_repository, AWSECommerceService, AWSECommerceService_proxy;
161+
162+const sACCES_ID = <Your AccesID here>;
163+
164+function ReadEntry(const APromp : string):string ;
165+begin
166+ Result := '';
167+ Write(APromp);
168+ while True do begin
169+ ReadLn(Result);
170+ Result := Trim(Result);
171+ if ( Length(Result) > 0 ) then
172+ Break;
173+ end;
174+end;
175+
176+var
177+ locService : AWSECommerceServicePortType;
178+ rqst : ItemSearch_Type;
179+ rsps : ItemSearchResponse_Type;
180+ rspsItem : Items_Type;
181+ i, j, k : Integer;
182+ itm : Item_Type;
183+begin
184+ SYNAPSE_RegisterHTTP_Transport();
185+ WriteLn('Web Services Toolkit Amazon sample');
186+ WriteLn('This sample demonstrates the "ItemSearch" method of the Amazon web service');
187+ WriteLn();
188+ rqst := ItemSearch_Type.Create();
189+ try
190+ locService := wst_CreateInstance_AWSECommerceServicePortType();
191+ rqst.AWSAccessKeyId := sACCES_ID;
192+ while True do begin
193+ rqst.Request.SetLength(1);
194+ rqst.Request[0].SearchIndex := ReadEntry('Enter the Search Index : ');
195+ rqst.Request[0].Availability := Available;
196+ rqst.Request[0].Count := 10;
197+ rqst.Request[0].MerchantId := 'Amazon';
198+ rqst.Request[0].ItemPage := 1;
199+ rqst.Request[0].Keywords := ReadEntry('Enter the Keywords : ');
200+ rsps := locService.ItemSearch(rqst);
201+ if ( rsps.OperationRequest.Errors.Length > 0 ) then begin
202+ WriteLn(Format('Errors ( %d ) : ',[rsps.OperationRequest.Errors.Length]));
203+ for i := 0 to Pred(rsps.OperationRequest.Errors.Length) do begin
204+ WriteLn(Format(' Error[%d] :',[i]));
205+ WriteLn(' ' + rsps.OperationRequest.Errors[i].Code);
206+ WriteLn(' ' + rsps.OperationRequest.Errors[i].Message);
207+ end;
208+ end else begin
209+ WriteLn(Format('Response ( %d ) : ',[rsps.Items.Length]));
210+ if Assigned(rsps) then begin
211+ for i := 0 to Pred(rsps.Items.Length) do begin
212+ rspsItem := rsps.Items[i];
213+ WriteLn(' TotalPages :' + IntToStr(rspsItem.TotalPages));
214+ WriteLn(' TotalResults :' + IntToStr(rspsItem.TotalResults));
215+ WriteLn(' Items :' + IntToStr(rspsItem._Item.Length));
216+ WriteLn('');
217+ for j := 0 to Pred(rspsItem._Item.Length) do begin
218+ itm := rspsItem._Item[j];;
219+ WriteLn(' ASIN :' + itm.ASIN);
220+ WriteLn(' DetailPageURL :' + itm.DetailPageURL);
221+ if Assigned(itm.ItemAttributes) then begin
222+ WriteLn(' Title :' + itm.ItemAttributes.Title);
223+ for k := 0 to Pred(itm.ItemAttributes.Author.Length) do begin
224+ WriteLn(' Author[ ' + IntToStr(k) + ' ] ' + itm.ItemAttributes.Author.Item[k]);
225+ end;
226+ WriteLn(' Manufacturer :' + itm.ItemAttributes.Manufacturer);
227+ WriteLn(' ProductGroup :' + itm.ItemAttributes.ProductGroup);
228+ end;
229+ WriteLn('');
230+ end;
231+ end;
232+ end else begin
233+ WriteLn('Unexpected service response : Invalid response');
234+ end;
235+ end;
236+ WriteLn();
237+ WriteLn();
238+ if ( UpperCase(ReadEntry('Continue ( Y/N ) :'))[1] <> 'Y' ) then
239+ Break;
240+ end;
241+ finally
242+ FreeAndNil(rqst);
243+ FreeAndNil(rsps);
244+ end;
245+ ReadLn;
246+end.
247+```
248+
249+As unidades base_service_intf, service_intf, soap_formatter, synapse_http_protocol, wst_resources_imp e metadata_repository são fornecidas com o toolkit. Abaixo está o resultado da execução da sessão de busca de “Freepascal” no índice de busca “All” (o serviço diferencia maiúscula/minúscula).
250+
251+ Web Services Toolkit Amazon sample
252+ This sample demonstrates the "ItemSearch" method of the Amazon web service
253+
254+ Enter the Search Index : All
255+ Enter the Keywords : Freepascal
256+ Response ( 1 ) :
257+ TotalPages :1
258+ TotalResults :9
259+ Items :9
260+
261+ ASIN :0470088702
262+ DetailPageURL : http://www.amazon.com/gp/redirect.html%3FASIN=0470088702%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0470088702%253FSubscriptionId=0W3H25JMMGBNBXSTQN82
263+ Title :Beginning Programming For Dummies (Beginning Programming f
264+ or Dummies)
265+ Author[ 0 ] Wallace Wang
266+ Manufacturer :For Dummies
267+ ProductGroup :Book
268+
269+ ASIN :0471375233
270+ DetailPageURL : http://www.amazon.com/gp/redirect.html%3FASIN=0471375233%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0471375233%253FSubscriptionId=0W3H25JMMGBNBXSTQN82
271+ Title :Assembly Language Step-by-step: Programming with DOS and Linux (with CD-ROM)
272+ Author[ 0 ] Jeff Duntemann
273+ Manufacturer :Wiley
274+ ProductGroup :Book
275+ (...)
276+
277+Isto encontra uma página contendo nove ítens. A função wst_CreateInstance_AWSECommerceServicePortType(), localizada no arquivo AWSECommerceService.pas cria uma instância do proxy baseada na informação do serviço contida no arquivo WSDL.
278+
279+``` pascal
280+Function wst_CreateInstance_AWSECommerceServicePortType(
281+ const AFormat : string;
282+ const ATransport : string
283+):AWSECommerceServicePortType;
284+Begin
285+ Result := TAWSECommerceServicePortType_Proxy.Create(
286+ 'AWSECommerceServicePortType',
287+ Aformat + GetServiceDefaultFormatProperties(TypeInfo(AWSECommerceServicePortType)),
288+ ATransport + 'address=' + GetServiceDefaultAddress(TypeInfo(AWSECommerceServicePortType))
289+ );
290+End;
291+```
292+
293+`Os fontes completos deste exemplo são entregues com o toolkit.`
294+
295+### Parâmetros de Connexão
296+
297+O formato geral é:
298+
299+ protocol:paramName=paramValue(;paramName=paramValue)*
300+
301+#### Parâmetros do Proxy HTTP
302+
303+Para HTTP os parâmetros suportados são:
304+
305+- Address **( required for a service supporting a unique address )**
306+- ProxyServer
307+- ProxyPort
308+- ProxyUsername
309+- ProxyPassword
310+
311+HTTP Connection through proxy are supported. Below is an example address string.
312+
313+``` pascal
314+ const
315+ sADDRESS = 'http:address=http://webservices.amazon.com/AWSECommerceService/2007-04-04'+
316+ ';ProxyServer=197.150.10.10;ProxyPort=9881'+
317+ ';ProxyUsername=inoussa;ProxyPassword=wst';
318+```
319+
320+The toolkit has two TCP implementations based on Synapse ( synapse_tcp_protocol.pas ) and ICS ( ics_tcp_protocol.pas ).
321+
322+#### Parâmetros da Conexão TCP
323+
324+Os parâmetros de TCP suportados são:
325+
326+- address ( required for a service supporting a unique address )
327+- Port
328+- target( the target service )
329+
330+Abaixo está um exemplo de uma string de endereço.
331+
332+``` pascal
333+ Const
334+ sADDRESS = 'TCP:Address=10.0.0.3;Port=1234;target=UserService';
335+```
336+
337+The toolkit has two TCP implementations based on Synapse ( synapse_tcp_protocol.pas ) and ICS ( ics_tcp_protocol.pas ).
338+
339+#### Parâmetros de Conexão LIBRARY ( LIB )
340+
341+A idéia por trás deste protocolo é poder hospedar serviços em bibliotecas dinâmicas (DLL/DSO). Isto pode ser visto como um framework de plug-in em que plug-ins (serviços) são fornecidos por bibliotecas dinâmicas. Os parâmetros para LIB suportados são:
342+
343+- FileName ( the DLL/SO filename )
344+- target( the target service )
345+
346+Abaixo um exemplo de uma string de endereço.
347+
348+``` pascal
349+ Const
350+ sADDRESS = 'LIB:FileName=..\library_server\lib_server.dll;target=UserService';
351+```
352+
353+The toolkit has one LIB implementation ( library_protocol.pas ).
354+
355+***As pastas sample contêm 3 projetos, user_client_console, tcp_server e library_server que demonstra os protocolos TCP e LIBRARY.***
356+
357+#### Serviço Multi-Endereço ( Endereço por operação )
358+
359+Certos serviços (como os serviços do eBay SOAP) usam um endereço por operação. O toolkit usa meta-dados estendidos (veja o capítulo sobre serviços de meta-dados abaixo) para configurar endereço de operação. O sample SOAP do eBay localizado na pasta test\eBay demonstra a configuração do endereço da operação.
360+
361+## Server Side ( service creation )
362+
363+### Overview
364+
365+Web Service Toolkit contains a server side framework for service creation. Key features are:
366+
367+- Service definition( interface ) is separated from implementation,
368+- Interface and implementations are not bound to message protocol,
369+- WSDL generation,
370+- Support for SOAP 1.1 and a binary protocol,
371+- The framework is not bound to a transport protocol.
372+- Easy to add support for application servers
373+
374+### Example
375+
376+In order to create a service, we have to :
377+
378+- define its interface,
379+- provide an implementation and register that one for the service,
380+- provide a binder that will route calls targeting the service to the implementation and register that one,
381+- host the service into an application server( TCP server, HTTP Server, LIBRARY server, ... ).
382+
383+#### Defining the service Interface
384+
385+We will use the interface defined below for our sample. The complete projects of the example is located in the folder “samples”.
386+
387+``` pascal
388+unit user_service_intf;
389+{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
390+interface
391+
392+uses SysUtils, Classes, TypInfo, base_service_intf, service_intf;
393+
394+const
395+ sNAME_SPACE = 'urn:UserService';
396+ sUNIT_NAME = 'user_service_intf';
397+
398+type
399+
400+ TUser = class;
401+ TUserArray = class;
402+
403+ TUserCategory = (
404+ Normal
405+ ,Admin
406+ );
407+
408+ TUser = class(TBaseComplexRemotable)
409+ private
410+ FCategory : TUserCategory;
411+ FUserName : string;
412+ FeMail : string;
413+ FPreferences : string;
414+ published
415+ property Category : TUserCategory read FCategory write FCategory;
416+ property UserName : string read FUserName write FUserName;
417+ property eMail : string read FeMail write FeMail;
418+ property Preferences : string read FPreferences write FPreferences;
419+ end;
420+
421+ TUserArray = class(TBaseObjectArrayRemotable)
422+ private
423+ function GetItem(AIndex: Integer): TUser;
424+ public
425+ class function GetItemClass():TBaseRemotableClass;override;
426+ property Item[AIndex:Integer] : TUser Read GetItem;Default;
427+ end;
428+
429+ UserService = interface(IInvokable)
430+ ['{CA6F6192-C3DE-4D9C-B3DF-E616376A0DC9}']
431+ function GetList():TUserArray;
432+ procedure Add(
433+ Const AUser : TUser
434+ );
435+ procedure Update(
436+ Const AUser : TUser
437+ );
438+ function Find(
439+ Const AName : string
440+ ):TUser;
441+ function Delete(
442+ Const AName : string
443+ ):boolean;
444+ end;
445+(...)
446+```
447+
448+#### Providing an implementation for the service
449+
450+“ws_helper” has options to generate proxy file, basic implementation skeleton file and a binder file ( see the following listing).
451+
452+ ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
453+ ws_helper [-uMODE] [-p] [-b] [-i] [-oPATH] inputFilename
454+ -u MODE Generate the pascal translation of the WSDL input file
455+ MODE value may be U for used types or A for all types
456+ -p Generate service proxy
457+ -b Generate service binder
458+ -i Generate service minimal implementation
459+ -o PATH Relative output directory
460+ -a PATH Absolute output directory
461+
462+Executing “ws_helper” with the -i and -b options as above will produce two files : user_service_intf_imp.pas and user_service_intf_binder.pas
463+
464+ ws_helper\ws_helper.exe -i -b -o. user_service_intf.pas
465+ ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
466+ Parsing the file : user_service_intf.pas
467+ Proxy file generation...
468+ Binder file generation...
469+ Implementation file generation...
470+ Metadata file generation...
471+ File "user_service_intf.pas" parsed succesfully..
472+
473+The user_service_intf_imp.pas unit contains a skeleton implementation class for the interface. It defines a procedure named RegisterUserServiceImplementationFactory. The procedure registers the class as the service implementation provider in the implementation registry.
474+
475+``` pascal
476+Unit user_service_intf_imp;
477+{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
478+Interface
479+Uses SysUtils, Classes,
480+ base_service_intf, server_service_intf, server_service_imputils,
481+ user_service_intf, cursor_intf;
482+
483+Type
484+ { TUserService_ServiceImp }
485+
486+ TUserService_ServiceImp=class(TBaseServiceImplementation,UserService)
487+ Protected
488+ function GetList():TUserArray;
489+ procedure Add(
490+ Const AUser : TUser
491+ );
492+ procedure Update(
493+ Const AUser : TUser
494+ );
495+ function Find(
496+ Const AName : string
497+ ):TUser;
498+ function Delete(
499+ Const AName : string
500+ ):boolean;
501+ End;
502+
503+ procedure RegisterUserServiceImplementationFactory();
504+
505+Implementation
506+(...)
507+
508+procedure TUserService_ServiceImp.Add(Const AUser : TUser);
509+var
510+ locObj : TUser;
511+Begin
512+ locObj := Find(AUser.UserName);
513+ if ( locObj <> nil ) then
514+ raise Exception.CreateFmt('Duplicated user : "%s"',[AUser.UserName]);
515+ locObj := TUser.Create();
516+ locObj.Assign(AUser);
517+ FUserList.Add(locObj);
518+End;
519+
520+procedure RegisterUserServiceImplementationFactory();
521+Begin
522+ GetServiceImplementationRegistry().Register(
523+ 'UserService',
524+TImplementationFactory.Create(TUserService_ServiceImp) as IServiceImplementationFactory);
525+End;
526+
527+(...)
528+```
529+
530+#### Providing a binder for the service.
531+
532+The binder's role is to:
533+
534+- unpack the incoming message,
535+- set up the call stack,
536+- make the call against the registered implementation,
537+- serialize the execution stack to create the return message.
538+
539+The user_service_intf_binder.pas unit generated by ws_helper, contains :
540+
541+- TUserService_ServiceBinder : the actual binder class,
542+- TUserService_ServiceBinderFactory a factory class for the binder and
543+- Server_service_RegisterUserServiceService : the binder factory registration procedure.
544+
545+The following code extract shows the unit interface part and a method handler of the binder.
546+
547+``` pascal
548+unit user_service_intf_binder;
549+{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
550+interface
551+uses SysUtils, Classes, base_service_intf, server_service_intf, user_service_intf;
552+
553+type
554+ TUserService_ServiceBinder=class(TBaseServiceBinder)
555+ Protected
556+ procedure GetListHandler(AFormatter:IFormatterResponse);
557+ procedure AddHandler(AFormatter:IFormatterResponse);
558+ procedure UpdateHandler(AFormatter:IFormatterResponse);
559+ procedure FindHandler(AFormatter:IFormatterResponse);
560+ procedure DeleteHandler(AFormatter:IFormatterResponse);
561+ Public
562+ constructor Create();
563+ End;
564+
565+ TUserService_ServiceBinderFactory = class(TInterfacedObject,IItemFactory)
566+ protected
567+ function CreateInstance():IInterface;
568+ End;
569+
570+ procedure Server_service_RegisterUserServiceService();
571+
572+(...)
573+
574+procedure TUserService_ServiceBinder.AddHandler(AFormatter:IFormatterResponse);
575+Var
576+ cllCntrl : ICallControl;
577+ tmpObj : UserService;
578+ callCtx : ICallContext;
579+ strPrmName : string;
580+ procName,trgName : string;
581+ AUser : TUser;
582+Begin
583+ callCtx := GetCallContext();
584+ TObject(AUser) := Nil;
585+
586+ strPrmName := 'AUser'; AFormatter.Get(TypeInfo(TUser),strPrmName,AUser);
587+ If Assigned(Pointer(AUser)) Then
588+ callCtx.AddObjectToFree(TObject(AUser));
589+
590+ tmpObj := Self.GetFactory().CreateInstance() as UserService;
591+ if Supports(tmpObj,ICallControl,cllCntrl) then
592+ cllCntrl.SetCallContext(GetCallContext());
593+
594+ tmpObj.Add(AUser);
595+
596+ procName := AFormatter.GetCallProcedureName();
597+ trgName := AFormatter.GetCallTarget();
598+ AFormatter.Clear();
599+ AFormatter.BeginCallResponse(procName,trgName);
600+ AFormatter.EndCallResponse();
601+
602+ callCtx := Nil;
603+End;
604+```
605+
606+#### Host the service into an application server.
607+
608+The application server's role is to route incoming service requests to the Web Service Toolkit runtime. For the runtime to process service requests :
609+
610+- The services and their implementations have to be registered ,
611+- The message protocol (SOAP, binary,...) have to be registered.
612+
613+The runtime interface is defined in the server_service_intf unit. This unit contains :
614+
615+- GetServerServiceRegistry, which returns the service registry,
616+- GetServiceImplementationRegistry which returns the service implementation registry,
617+- GetFormatterRegistry which returns the message format registry and
618+- HandleServiceRequest which is the unique entry point for request processing.
619+
620+The toolkit is provided with a simple TCP server ( using Synapse components ) hosting the sample UserService service defined early in this document and located in the \samples\tcp_server folder. The complete source files of the sample is in that directory and the client application in the \samples\user_client_console sub folder. The registrations are done in the application main routine as printed above:
621+
622+``` pascal
623+program tcp_server;
624+{$INCLUDE wst.inc}
625+uses
626+ Classes, SysUtils,
627+ base_service_intf, server_service_soap,
628+ base_binary_formatter, server_binary_formatter,
629+ metadata_service, metadata_service_imp, metadata_service_binder,
630+ synapse_tcp_server,
631+ user_service_intf, user_service_intf_binder, user_service_intf_imp , imp_helper;
632+var
633+ listnerThread : TServerListnerThread;
634+begin
635+ SetLogger(TConsoleLogger.Create());
636+
637+ Server_service_RegisterBinaryFormat();
638+ Server_service_RegisterSoapFormat();
639+ Server_service_RegisterWSTMetadataServiceService();
640+ RegisterWSTMetadataServiceImplementationFactory();
641+
642+ Server_service_RegisterUserServiceService();
643+ RegisterUserServiceImplementationFactory();
644+
645+ Logger().Log('WST sample TCP Server listning on "%s"',[sSERVER_PORT]);
646+ Logger().Log('Hit <enter> to stop.');
647+ listnerThread := TServerListnerThread.Create();
648+ ReadLn;
649+end.
650+```
651+
652+Server_service_RegisterUserServiceService located in the user_service_intf_binder unit ( generated by ws_helper ) registers the UserService service by calling in turn GetServerServiceRegistry:
653+
654+``` pascal
655+procedure Server_service_RegisterUserServiceService();
656+Begin
657+ GetServerServiceRegistry().Register(
658+ 'UserService',TUserService_ServiceBinderFactory.Create() as IitemFactory
659+ );
660+End;
661+```
662+
663+Server_service_RegisterSoapFormat located in the server_service_soap unit ( provided by the toolkit ) registers the SOAP implementation by calling in turn GetFormatterRegistry:
664+
665+``` pascal
666+ procedure Server_service_RegisterSoapFormat();
667+ begin
668+ GetFormatterRegistry().Register(
669+ sSOAP_CONTENT_TYPE,
670+ TSimpleItemFactory.Create(TSOAPFormatter) as IitemFactory
671+ );
672+ RegisterStdTypes();
673+ end;
674+```
675+
676+Server_service_RegisterBinaryFormat located in the server_binary_formatter unit ( provided by the toolkit ) registers the Binary message implementation by calling in turn GetFormatterRegistry:
677+
678+``` pascal
679+ procedure Server_service_RegisterBinaryFormat();
680+ begin
681+ GetFormatterRegistry().Register(
682+ sCONTENT_TYPE,
683+ TBinaryFormatterFactory.Create() as IitemFactory
684+ );
685+ end;
686+```
687+
688+Incoming requests are processed in the client thread TClientHandlerThread.Execute() method (located in the synapse_tcp_server.pas file) invoking the HandleServiceRequest() method.
689+
690+``` pascal
691+procedure TClientHandlerThread.Execute();
692+var
693+ wrtr : IDataStore;
694+ rdr : IDataStoreReader;
695+ buff, trgt,ctntyp : string;
696+ rqst : IRequestBuffer;
697+ i : PtrUInt;
698+begin
699+ FInputStream := TMemoryStream.Create();
700+ FOutputStream := TMemoryStream.Create();
701+ FSocketObject := TTCPBlockSocket.Create();
702+ try
703+ FSocketObject.RaiseExcept := True;
704+ try
705+ FSocketObject.Socket := FSocketHandle;
706+ FSocketObject.GetSins();
707+ while not Terminated do begin
708+ FOutputStream.Size := 0;
709+ if ( ReadInputBuffer() >= SizeOf(LongInt) ) then begin
710+ rdr := CreateBinaryReader(FInputStream);
711+ trgt := rdr.ReadStr();
712+ ctntyp := rdr.ReadStr();
713+ buff := rdr.ReadStr();
714+ rdr := nil;
715+ FInputStream.Size := 0;
716+ FInputStream.Write(buff[1],Length(buff));
717+ FInputStream.Position := 0;
718+ rqst := TRequestBuffer.Create(trgt,ctntyp,FInputStream,FOutputStream);
719+ HandleServiceRequest(rqst);
720+ i := FOutputStream.Size;
721+ SetLength(buff,i);
722+ FOutputStream.Position := 0;
723+ FOutputStream.Read(buff[1],i);
724+ FOutputStream.Size := 0;
725+ wrtr := CreateBinaryWriter(FOutputStream);
726+ wrtr.WriteStr(buff);
727+ SendOutputBuffer();
728+ ClearBuffers();
729+ end;
730+ end;
731+ except
732+ on e : Exception do begin
733+ Logger().Log('Error : ThreadID = %d; Message = %s',[Self.ThreadID,e.Message]);
734+ end;
735+ end;
736+ finally
737+ FreeAndNil(FSocketObject);
738+ end;
739+end;
740+```
741+
742+In order to give it a try one have to :
743+
744+- compile the server ( \samples\tcp_server\tcp_server.lpi it is a console program),
745+- compile the client application ( \samples\user_client_console\user_client_console.lpi ),
746+- execute the server and start listening,
747+- execute the client.
748+
749+### WSDL generation
750+
751+Services in the toolkit are organized into meta data repositories. Conceptually a repository corresponds :
752+
753+- at compile time to the pascal unit containing the service definition
754+- at runtime to a name space.
755+
756+The repository is the toolkit WSDL generation unit.
757+
758+#### The Metadata Service
759+
760+The toolkit is provided with an easy to use metadata service implementation which in turn uses the raw interface defined in the metadata_repository unit (see above). A Lazarus GUI client application is located in the **tests\metadata_browser** folder.
761+
762+#### WSDL generation API
763+
764+The **metadata_wsdl** pascal unit contains the **GenerateWSDL** function for WSDL generation from a repository (see the signature below).
765+
766+``` pascal
767+ PServiceRepository = ^TServiceRepository;
768+ TServiceRepository = record
769+ NameSpace : ShortString;
770+ Name : ShortString;
771+ RootAddress : ShortString;
772+ ServicesCount : Byte;
773+ Services : PService;
774+ end;
775+
776+ procedure GenerateWSDL(AMdtdRep : PServiceRepository; ADoc : TDOMDocument);
777+```
778+
779+##### WSDL Customization
780+
781+The WSDL generation is based on the **IWsdlTypeHandler** and the **IWsdlTypeHandlerRegistry** interfaces located in the **metadata_wsdl** unit. In order to customize the generated WSDL, one has to provide a class implementing the **IWsdlTypeHandler** interface. Then that class has to be registered in the registry. The **metadata_wsdl** unit contains implementations for pascal enumerations, **TBaseComplexRemotable** descendants, and **TBaseArrayRemotable** descendants.
782+
783+#### Sample
784+
785+A functional sample project is located under **\samples\http_server** . It is an Indy base http server.
786+
787+### Services Extensions
788+
789+Services extensions provide a mean to hook into all the services request processing stages. Services extensions may be used, for example, to implement authentication, request logging, data compression, etc. The **IServiceExtension** bellow is the interface used by the toolkit runtime to call services extensions.
790+
791+``` pascal
792+ TMessageStage = (
793+ msAfterDeserialize, msAfterSerialize, msBeforeDeserialize, msBeforeSerialize
794+ );
795+
796+ IServiceExtension = interface
797+ ['{E192E6B3-7932-4D44-A8AC-135D7A0B8C93}']
798+ procedure ProcessMessage(
799+ const AMessageStage : TMessageStage;
800+ ACallContext : ICallContext;
801+ AMsgData : IInterface
802+ );
803+ end;
804+```
805+
806+The **AMsgData** parameter actual type depends on the message processing state and corresponds to :
807+
808+- **IRequestBuffer** on "msBeforeDeserialize" and "msAfterSerialize"
809+- **IFormatterResponse** on "msAfterDeserialize" and "msBeforeSerialize"
810+
811+These types are located in the **server_service_intf** unit. Extensions have to be registered in the extensions registry ( located in the server_service_intf unit ) printed bellow
812+
813+``` pascal
814+ IServiceExtensionRegistry = Interface
815+ ['{68DC78F1-E6CF-4D6B-8473-75288794769C}']
816+ function Find(const AName : string):IServiceExtension;
817+ procedure Register(
818+ const AName : string;
819+ AFactory : IItemFactory
820+ );
821+ end;
822+```
823+
824+In order for an service implementation to use a service extension, it has to register himself to that extension. To that end, the IServiceImplementationFactory interface provides the RegisterExtension method. A complete sample is included in the \samples\http_server sample ( implemented in **\samples\logger_extension.pas** ).
825+
826+## Services meta data
827+
828+Services in the toolkit are organized into ***meta data repositories( see the “services's meta data” below )***. Conceptually a repository corresponds :
829+
830+- at compile time to the pascal unit containing the service definition
831+- at runtime to a name space.
832+
833+The **ws_helper** tool, when parsing the interface definition file, records the meta data of the services contained in the file to a Lazarus resource file. The resource file is then embedded into the generated binder's unit file( see the unit **“initialization”** part ). At runtime the service's recorded meta data are accessible through the interface **IModuleMetadataMngr** defined in the **metadata_repository** unit ( see below ). The **GetModuleMetadataMngr** function defined in the same unit returns an instance of an object supporting that interface.
834+
835+``` pascal
836+ IModuleMetadataMngr = interface
837+ ['{B10ACF6A-A599-45A3-B083-BEEFB810C889}']
838+ function IndexOfName(const ARepName : shortstring):Integer;
839+ function GetCount():Integer;
840+ function GetRepositoryName(const AIndex : Integer):shortstring;
841+ procedure SetRepositoryNameSpace(const ARepName,ANameSpace : shortstring);
842+ function LoadRepositoryName(
843+ const ARepName,ARootAddress : shortstring;
844+ out ARepository : PServiceRepository
845+ ):Integer;
846+ procedure ClearRepository(var ARepository : PServiceRepository);
847+ procedure SetServiceCustomData(
848+ const ARepName : shortstring;
849+ const AServiceName : shortstring;
850+ const ADataName,
851+ AData : string
852+ );
853+ procedure SetOperationCustomData(
854+ const ARepName : shortstring;
855+ const AServiceName : shortstring;
856+ const AOperationName : shortstring;
857+ const ADataName,
858+ AData : string
859+ );
860+ function GetServiceMetadata(const ARepName,AServiceName : shortstring) : PService;
861+ procedure ClearServiceMetadata(var AService : PService);
862+ end;
863+```
864+
865+### Extended Meta data
866+
867+The meta data interface provides a way to add custom data to recorded ones. Services's metadata can be set through **SetServiceCustomData**, operation's metadata be set through the **SetOperationCustomData** method. A repository's extended meta data has to be registered after the service meta data recorded in the resource file have been registered. So for client application the generated proxy unit contains a conditional code fragment to call a registration procedure like showed below for the eBay sample located in the **tests\ebay** folder. The procedure name is obtained from the interface unit name ( the repository's name ) : **Register_%UNIT_NAME%_ServiceMetadata** .
868+
869+``` pascal
870+ initialization
871+ {$i ebay.lrs}
872+
873+ {$IF DECLARED(Register_ebay_ServiceMetadata)}
874+ Register_ebay_ServiceMetadata();
875+ {$ENDIF}
876+ End.
877+```
878+
879+## Headers support
880+
881+### The THeaderBlock class
882+
883+``` pascal
884+ THeaderBlock = class(TBaseComplexRemotable)
885+ public
886+ property Direction : THeaderDirection read FDirection write FDirection;
887+ property Understood : Boolean read FUnderstood write FUnderstood;
888+ published
889+ property mustUnderstand : Integer read FmustUnderstand write SetmustUnderstand
890+ stored HasmustUnderstand;
891+ end;
892+```
893+
894+The **THeaderBlock** showed above ( the private part has been omitted for brevity), located in the **base_service_intf** unit, is the root class all header classes are derived from.. The **Direction** property indicate whether it is an incoming header or an outgoing one. The **mustUnderstand** property define whether the header is a mandatory one.
895+
896+### Defining header class
897+
898+Soap headers are derived from the **THeaderBlock** base class located in the **base_service_intf** unit. They have to be registered in the type registry. Below is reproduced an header example extracted from the “calculator” sample project.
899+
900+``` pascal
901+ TCalcHeader = class(THeaderBlock)
902+ published
903+ property Login : string read FLogin write FLogin;
904+ property Password : string read FPassword write FPassword;
905+ property WantedPrecision : Integer read FWantedPrecision write FWantedPrecision;
906+ end;
907+```
908+
909+### The ICallContext interface
910+
911+``` pascal
912+ ICallContext = Interface
913+ ['{855EB8E2-0700-45B1-B852-2101023200E0}']
914+ procedure AddObjectToFree(const AObject : TObject);
915+ procedure Clear();
916+ function AddHeader(
917+ const AHeader : THeaderBlock;
918+ const AKeepOwnership : Boolean
919+ ):Integer;
920+ function GetHeaderCount(const ADirections : THeaderDirections):Integer;
921+ function GetHeader(const AIndex : Integer) : THeaderBlock;
922+ procedure ClearHeaders(const ADirection : THeaderDirection);
923+ End;
924+```
925+
926+The **ICallContext** interface defined in the **base_service_intf** unit represents the service call context. The **AddHeader** method allows headers sending while the **GetHeader** method retrieves header in the call context.
927+
928+### Client side headers
929+
930+An **ICallContext** reference may be obtained from the current service proxy instance simply by querying it for that interface as showed in the code fragment below extracted from the “calculator” client example project.
931+
932+``` pascal
933+ var
934+ ch : TCalcHeader;
935+ hdrs : ICallContext;
936+ begin
937+ FObj := TCalculator_Proxy.Create('Calculator', edtFormat.Text, edtAddress.Text);
938+
939+ ch := TCalcHeader.Create();
940+ ch.mustUnderstand := 1;
941+ ch.Login := 'azerty';
942+ ch.Password := 'qwerty';
943+ ch.WantedPrecision := 1210;
944+ hdrs := FObj as ICallContext;
945+ hdrs.AddHeader(ch,true);
946+```
947+
948+A header may be made mandatory by setting its **mustUnderstand** property to 1 as in the code above.
949+
950+### Server side headers
951+
952+#### The ICallControl interface
953+
954+``` pascal
955+ ICallControl = interface
956+ ['{7B4B7192-EE96-4B52-92C7-AE855FBC31E7}']
957+ procedure SetCallContext(ACallContext : ICallContext);
958+ function GetCallContext():ICallContext;
959+ end;
960+```
961+
962+The **ICallControl** interface, located in the **server_service_intf** unit, is used by the toolkit runtime to share the executing call environment with service implementation classes. When the runtime is about to issue a call against a implementation class instance, it queries that instance for ICallControl interface support; If the implementation has ICallControl interface support then the obtained reference is used to set the call context through the **SetCallContext** method. The implementation instance can then access the call context by calling the **GetCallContex** method. The toolkit provides the **TBaseServiceImplementation** class which has support for the ICallControl interface and can be used as a base implementation class. It is the base class used by the **ws_helper** generated skeleton implementation class when invoked with the **-i** command line option. The method printed bellow, extracted from the calculator sample service demonstrates the access to headers for read and write.
963+
964+``` pascal
965+ function TCalculator_ServiceImp.AddInt(
966+ Const A : Integer;
967+ Const B : Integer
968+ ):TBinaryArgsResult;
969+ var
970+ hdr : TCalcResultHeader;
971+ h : TCalcHeader;
972+ cc : ICallContext;
973+ Begin
974+ hdr := TCalcResultHeader.Create();
975+ cc := GetCallContext();
976+ if Assigned(cc) and ( cc.GetHeaderCount([hdIn]) > 0 ) and ( cc.GetHeader(0).InheritsFrom(TCalcHeader) ) then begin
977+ h := cc.GetHeader(0) as TCalcHeader;
978+ h.Understood := True;
979+ hdr.Assign(h);
980+ end;
981+ hdr.TimeStamp := DateTimeToStr(Now());
982+ hdr.SessionID := 'testSession';
983+ cc.AddHeader(hdr,True);
984+ hdr := nil;
985+ Result := TBinaryArgsResult.Create();
986+ Try
987+ Result.Arg_OP := '+';
988+ Result.Arg_OpEnum := coAdd;
989+ Result.Arg_A := A;
990+ Result.Arg_B := B;
991+ Result.Arg_R := A + B;
992+ Result.Comment := 'Doing an + operation';
993+ Except
994+ FreeAndNil(Result);
995+ Raise;
996+ End;
997+ End;
998+```
999+
1000+## SOAP Specific
1001+
1002+### Binding style
1003+
1004+The binding style is used to indicate whether the service is **RPC** oriented or **Document** oriented.
1005+
1006+#### Client side
1007+
1008+The binding style may be specified in the SOAP protocol string on the creation of a service proxy. The default value for the binding style is **RPC**. Below is printed a sample code that demonstrates the use of Document style.
1009+
1010+``` pascal
1011+ locService := TSampleService_Proxy.Create(
1012+ 'SampleService',
1013+ 'SOAP:Style=Document;EncodingStyle=Litteral',
1014+ 'http:Address=http://127.0.0.1/services/SampleService'
1015+ );
1016+```
1017+
1018+#### Server side
1019+
1020+Currently services created with the toolkit use the **RPC** binding style.
1021+
1022+### Encoding style
1023+
1024+The encoding style indicates the rules used to encode types in XML. Supported values are **Encoded** and **Litteral**.
1025+
1026+#### Client side
1027+
1028+The encoding style may be specified in the SOAP protocol string on the creation of a service proxy. The default value for the encoding style is **Encoded**. The above sample demonstrates the use of **Litteral** style.
1029+
1030+#### Server side
1031+
1032+Currently services created with the toolkit use the **Encoded** encoding style.
1033+
1034+## Provided examples
1035+
1036+The samples are located under the **“tests”** folder.
1037+
1038+### Client side examples ( tested on Windows XP and Ubuntu )
1039+
1040+- UserService, samples\http_server, samples\tcp_server, samples\user_client_console, sample\library_server : the client console uses the three client and server protocols (HTTP, TCP, LIBRARY)
1041+- Google sample : It demonstrates use of class and array data types .
1042+- Metadata Browser : This sample demonstrates use of class and array data types and mainly the toolkit metadata service.
1043+- eBay sample, this sample uses OpenSLL which can be found at <http://www.openssl.org> and SYNAPSE ( <http://www.ararat.cz/synapse/> ).
1044+- \samples\delphi\user_client_console : Delphi ( compile with Delphi 7) client sample. Used protocol : the TCP, HTTP, LIBRARY; Used format : BINARY.
1045+
1046+### Server side examples
1047+
1048+- samples\tcp_server : This is a sample TCP server based on the Synapse components. It uses the UserService.
1049+- samples\http_server : This is a sample HTTP server based on the Indy10 components. It uses the UserService and the toolkit metadata service. It demonstrates the WSDL generation.
1050+- samples\apache_module : Apache module sample, this sample demonstrates the hosting of the toolkit into the Apache HTTP web server. It is based on Sekelsenmat's Apache headers translation. It uses the UserService service and the toolkit metadata service. It demonstrates the WSDL generation.
1051+
1052+## Status
1053+
1054+The toolkit is usable for simple types and for class types. The serialization is designed to allow customization of basic types and class types by implementing classes derived from “TBaseRemotable”. This classes have to be registered in the type registry.
1055+
1056+### Serialization
1057+
1058+The serialization is based on the IFormatterBase interface located in the **base_service_intf** unit.
1059+
1060+The toolkit has two serializers implementations : the **SOAP serializer** and a **Binary serializer**.
1061+
1062+#### *SOAP serializer*
1063+
1064+The SOAP serializer implements SOAP 1.1. It has support for the following pascal types:
1065+
1066+- Available integers :
1067+ - Byte mapped to unsignedByte
1068+ - ShortInt mapped to byte
1069+ - SmallInt mapped to short
1070+ - Word mapped to unsignedShort
1071+ - LongInt mapped to int
1072+ - LongWord mapped to unsignedInt
1073+ - Int64 mapped to long
1074+ - QWord mapped to int
1075+
1076+- String mapped to string
1077+- Boolean mapped to boolean
1078+- Enumerations mapped to their string representation
1079+
1080+- Float types :
1081+ - Single mapped to float
1082+ - Double mapped to double
1083+ - Extended mapped to double
1084+ - Currency mapped to float
1085+
1086+- Object (class instances, not TP ones ) : The toolkit has support for instances of classes derived from TBaseRemotable. TBaseRemotable is the base class used by the formatter interface to allow customization of the serialization. The toolkit provides the TBaseComplexRemotable class which implements serialization for its ( or its descendants ) published properties.
1087+
1088+#### *Binary serializer*
1089+
1090+The Binary serializer is more efficient in time and space compared to the SOAP serializer . It uses big endian to stream data. This serializer has been tested with Delphi 7 ( \samples\delphi\user_client_console ) It has support for the following pascal types:
1091+
1092+- Available integers :
1093+ - Byte
1094+ - ShortInt
1095+ - SmallInt
1096+ - Word
1097+ - LongInt
1098+ - LongWord
1099+ - Int64
1100+ - QWord
1101+
1102+- String
1103+- Boolean
1104+- Enumerations
1105+
1106+- Float types :
1107+ - Single
1108+ - Double
1109+ - Extended
1110+ - Currency
1111+
1112+- Object (class instances, not TP ones ) :The toolkit has support for instances of classes derived from **TBaseRemotable**. TBaseRemotable is the base class used by the formatter interface to allow customization of the serialization. The toolkit provides the TBaseComplexRemotable class which implements serialization for its ( or its descendants ) published properties.
1113+
1114+#### *Class type serialization*
1115+
1116+The toolkit has support for instances of classes derived from **TBaseRemotable**. TBaseRemotable is the abstract base class used by the formatter interface to allow customization of the serialization. The toolkit provides the TBaseComplexRemotable class which implements serialization for its descendants classes published properties. It also provides TBaseObjectArrayRemotable class for serialization of array of TBaseRemotable descendant classes.
1117+
1118+##### The root **“TBaseRemotable”** class
1119+
1120+``` pascal
1121+ TBaseRemotable = class(TPersistent)
1122+ Public
1123+ constructor Create();virtual;
1124+ class procedure Save(
1125+ AObject : TBaseRemotable;
1126+ AStore : IFormatterBase;
1127+ Const AName : String;
1128+ Const ATypeInfo : PTypeInfo
1129+ );virtual;abstract;
1130+ class procedure Load(
1131+ Var AObject : TObject;
1132+ AStore : IFormatterBase;
1133+ var AName : String;
1134+ const ATypeInfo : PTypeInfo
1135+ );virtual;abstract;
1136+ End;
1137+```
1138+
1139+**TBaseRemotable** is the abstract base class used by the formatter interface to allow customization of the serialization. This class defines a **virtual constructor** and mainly two(2) virtual abstract class methods :
1140+
1141+- **Save** : this method is called when the toolkit needs to serialize the **AObject** parameter.
1142+- **Load**: this method is called when the toolkit needs to un-serialize to the **AObject** parameter.
1143+
1144+##### The “TBaseComplexRemotable” serialization
1145+
1146+``` pascal
1147+ TBaseComplexRemotable = class(TAbstractComplexRemotable)
1148+ public
1149+ class procedure Save(
1150+ AObject : TBaseRemotable;
1151+ AStore : IFormatterBase;
1152+ const AName : string;
1153+ const ATypeInfo : PTypeInfo
1154+ );override;
1155+ class procedure Load(
1156+ var AObject : TObject;
1157+ AStore : IFormatterBase;
1158+ var AName : string;
1159+ const ATypeInfo : PTypeInfo
1160+ );override;
1161+ class procedure RegisterAttributeProperty(const AProperty : shortstring);virtual;
1162+ class procedure RegisterAttributeProperties(const APropertList : array of shortstring);virtual;
1163+ class function IsAttributeProperty(const AProperty : shortstring):Boolean;
1164+ procedure Assign(Source: TPersistent); override;
1165+ end;
1166+```
1167+
1168+**TBaseComplexRemotable** implements serialization for its descendants classes published properties. The serialization is based on runtime type information (RTTI) and can be customized to:
1169+
1170+- ignore always some published properties.
1171+- ignore conditionally some published properties.
1172+
1173+The following class shows a the serialization's customization sample.
1174+
1175+``` pascal
1176+ TSampleClass = class(TBaseComplexRemotable)
1177+ private
1178+ FProp_Always: Integer;
1179+ FProp_Never: Integer;
1180+ FProp_Optional: Integer;
1181+ function GetStoredProp_Optional: boolean;
1182+ published
1183+ //This property will always be serialized
1184+ property Prop_Always : Integer read FProp_Always write FProp_Always;
1185+ //This property will never be serialized
1186+ property Prop_Never : Integer read FProp_Never write FProp_Never stored False;
1187+ //This property will be serialized if "Self.GetStoredProp_Optional() = True"
1188+ property Prop_Optional : Integer read FProp_Optional write FProp_Optional stored GetStoredProp_Optional;
1189+ End;
1190+```
1191+
1192+- Attribute properties
1193+
1194+TBaseComplexRemotable allows properties serialization as attributes. Theses properties have to be registered as such with the RegisterAttributeProperty class method or RegisterAttributeProperties one.
1195+
1196+##### TBaseComplexRemotable
1197+
1198+**TBaseComplexSimpleContentRemotable** provides implementation for the “XML Schema” complex types which extend simple types with attributes. The following example illustrates this :
1199+
1200+``` xml
1201+ <xs:complexType name="DecimalWithUnits">
1202+ <xs:simpleContent>
1203+ <xs:extension base="xs:decimal">
1204+ <xs:attribute name="Units" type="xs:string"
1205+ use="required"/>
1206+ </xs:extension>
1207+ </xs:simpleContent>
1208+ </xs:complexType>
1209+```
1210+
1211+This type will be translate by ws_helper to Pascal as
1212+
1213+``` pascal
1214+ DecimalWithUnits = class(TComplexFloatExtendedContentRemotable)
1215+ private
1216+ FUnits : string;
1217+ published
1218+ property Units : string read FUnits write FUnits;
1219+ end;
1220+```
1221+
1222+using the predefined types ( in base_service_intf.pas )
1223+
1224+``` pascal
1225+ TBaseComplexSimpleContentRemotable =
1226+ class(TAbstractComplexRemotable)
1227+ protected
1228+ class procedure SaveValue(
1229+ AObject : TBaseRemotable;
1230+ AStore : IformatterBase
1231+ );virtual;abstract;
1232+ class procedure LoadValue(
1233+ var AObject : TObject;
1234+ AStore : IformatterBase
1235+ );virtual;abstract;
1236+ public
1237+ class procedure Save(
1238+ AObject : TBaseRemotable;
1239+ AStore : IFormatterBase;
1240+ const AName : string;
1241+ const ATypeInfo : PTypeInfo
1242+ );override;
1243+ class procedure Load(
1244+ var AObject : TObject;
1245+ AStore : IFormatterBase;
1246+ var AName : string;
1247+ const ATypeInfo : PTypeInfo
1248+ );override;
1249+ end;
1250+
1251+ TComplexFloatExtendedContentRemotable =
1252+ class(TBaseComplexSimpleContentRemotable)
1253+ private
1254+ FValue: Extended;
1255+ protected
1256+ class procedure SaveValue(
1257+ AObject : TBaseRemotable;
1258+ AStore : IformatterBase
1259+ );override;
1260+ class procedure LoadValue(
1261+ var AObject : TObject;
1262+ AStore : IformatterBase
1263+ );override;
1264+ public
1265+ property Value : Extended read FValue write FValue;
1266+ end;
1267+```
1268+
1269+An instance of this type looks like the one below. Every attribute must be registered using the **RegisterAttributeProperty()** method. The toolkit provides class for Pascal basic types( TComplexInt8UContentRemotable, TComplexInt8SContentRemotable, TComplexInt16SContentRemotable, ...).
1270+
1271+``` xml
1272+ <example Units = "meter">
1273+ 12.10
1274+ </example>
1275+```
1276+
1277+##### Provided array implementations
1278+
1279+The toolkit provides array implementation for basic types ( in the **base_service_intf** unit ) listed bellow. The implementations are based on the serialization's customization.
1280+
1281+- Available integers :
1282+ - Byte **TArrayOfInt8URemotable**
1283+ - ShortInt **TArrayOfInt8SRemotable**
1284+ - SmallInt **TArrayOfInt16SRemotable**
1285+ - Word **TArrayOfInt16URemotable**
1286+ - LongInt **TArrayOfInt32SRemotable**
1287+ - LongWord **TArrayOfInt32URemotable**
1288+ - Int64 **TArrayOfInt64SRemotable**
1289+ - Qword **TArrayOfInt64URemotable**
1290+- String **TarrayOfStringRemotable**( AnsiString )
1291+- Boolean **TArrayOfBooleanRemotable**
1292+- Float types :
1293+ - Single **TArrayOfFloatSingleRemotable**
1294+ - Double **TArrayOfFloatDoubleRemotable**
1295+ - Extended **TArrayOfFloatExtendedRemotable**
1296+ - Currency **TArrayOfFloatCurrencyRemotable**
1297+
1298+The toolkit array's implementation support “embedded” array serialization. This type of array occurs typically with types like the following one ( the "ResponseGroup" may be repeated ):
1299+
1300+``` xml
1301+ <xs:complexType name="CustomerContentSearchRequest">
1302+ <xs:sequence>
1303+ <xs:element name="CustomerPage" type="xs:positiveInteger"
1304+ minOccurs="0"/>
1305+ <xs:element name="Email" type="xs:string" minOccurs="0"/>
1306+ <xs:element name="Name" type="xs:string" minOccurs="0"/>
1307+ <xs:element name="ResponseGroup" type="xs:string"
1308+ minOccurs="0" maxOccurs="unbounded"/>
1309+ </xs:sequence>
1310+ </xs:complexType>
1311+```
1312+
1313+which could be instantiated as
1314+
1315+``` xml
1316+ <search>
1317+ <CustomerPage> 1 </CustomerPage>
1318+ <Name>Sample name</Name>
1319+ <ResponseGroup>Group 1</ResponseGroup>
1320+ <ResponseGroup>Group 2</ResponseGroup>
1321+ <ResponseGroup>Group 3</ResponseGroup>
1322+ </search>
1323+```
1324+
1325+This type will be translate to Pascal by ws_helper as (the private and protected parts are omitted to be short)
1326+
1327+``` pascal
1328+(...)
1329+ CustomerContentSearchRequest_ResponseGroupArray =
1330+ class(TBaseSimpleTypeArrayRemotable)
1331+ public
1332+ class function GetItemTypeInfo():PTypeInfo;override;
1333+ procedure SetLength(const ANewSize : Integer);override;
1334+ property Item[AIndex:Integer] : string
1335+ read GetItem write SetItem; default;
1336+ end;
1337+
1338+ CustomerContentSearchRequest = class(TBaseComplexRemotable)
1339+ published
1340+ property CustomerPage : positiveInteger
1341+ read FCustomerPage
1342+ write FCustomerPage stored HasCustomerPage;
1343+ property Email : string
1344+ read FEmail
1345+ write FEmail
1346+ stored HasEmail;
1347+ property Name : string read FName write FName stored HasName;
1348+ property ResponseGroup :
1349+ CustomerContentSearchRequest_ResponseGroupArray
1350+ read FResponseGroup
1351+ write FResponseGroup;
1352+ end;
1353+
1354+implementation
1355+(...)
1356+ GetTypeRegistry().ItemByTypeInfo[
1357+ TypeInfo(CustomerContentSearchRequest_ResponseGroupArray)]
1358+ .RegisterExternalPropertyName(sARRAY_STYLE,sEmbedded);
1359+(...)
1360+```
1361+
1362+The last instruction set the array style to “Embedded” and so the SOAP formatter will serialize the array accordingly.
1363+
1364+#### *Test cases*
1365+
1366+The toolkit uses FPCUnit for test cases. The test project is located in the **\tests\test_suite** folder.
1367+
1368+## TODO
1369+
1370+### TODO Common
1371+
1372+- Simple type support in headers
1373+- Header support for the binary format
1374+- True Attribute serialization support for the binary format
1375+
1376+### TODO Client-Side
1377+
1378+- ~~Basic array support~~ for SOAP
1379+- ~~Basic array support~~ for Binary format
1380+- XML-RPC formatter
1381+- More documentation and samples !
1382+ - ~~eBay basic client~~ : demonstrates the **GetPopularKeywords** operation call
1383+ - ~~"Amazon E-Commerce Service" sample~~
1384+- ~~WSDL to pascal compiler~~
1385+- Enhance the parser
1386+ - ~~To allow comments in the input file of ws_helper~~ : **{}** comment style are now supported
1387+- Client side services extensions
1388+
1389+### TODO Server-Side
1390+
1391+Extend the toolkit to Server side for :
1392+
1393+- ~~SOAP~~
1394+- ~~Binary serialization~~
1395+- ~~Bytes ordering in binary serialization : alaways use big-endian~~
1396+- XML-RPC
1397+- ~~TCP transport~~ ( first implementation).
1398+- ~~WSDL generation~~
1399+- More documentation and samples !
1400+- ~~Apache support~~ : services as Apache modules using Sekelsenmat 's \[\[:fpc:FPC_and_Apache_Modules \| Apache headers translation
1401+
1402+\]\]
1403+
1404+- - See the **apache_module** sample located in the ***tests\apache_module*** folder
1405+- Classes inheritance generation in WSDL
1406+
1407+## Licence
1408+
1409+- The support units are provided under modified LGPL
1410+- ws_helper sources are provided under GPL 2 ( or later version )
1411+
1412+## Download & NewsGroup
1413+
1414+The lastest version can be found at <http://inoussa12.googlepages.com/webservicetoolkitforfpc%26lazarus> and from [Lazarus-CCR sourceforge site](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=196167).\
1415+The "Web Service Toolkit" now has a newsgroup hosted at <news://news.dxmachine.com/public.wst>
1416+
1417+## Changes Log
1418+
1419+### Version 0.4
1420+
1421+- WSDL to Pascal translation support in "ws_helper"
1422+- new TCP transport implementation ( using synapse library ) in synapse_tcp_protocol.pas
1423+- new library protocol implementation in library_protocol.pas
1424+- TCP server implementation ( using synapse library ) in synapse_tcp_server.pas
1425+- Delphi : first binary format support
1426+- Embedded array support
1427+- Object Pascal reserved words can now be used as service methods's name, enumeration, ... ( see TTypeRegistryItem.RegisterExternalPropertyName() )
1428+- The toolkit can now be used with FPC without Lazarus
1429+- "Amazon E-Commerce Service" sample
1430+- Bugs Fixes.
1431+
1432+### Version 0.3.1 ( 21 August 2006 )
1433+
1434+- Apache Web Server support : services hosting as Apache's module.
1435+ - See the **apache_module** sample located in the ***tests\apache_module*** folder.
1436+- Important : In the connection string the address token was mis-spelled as **adress** (one letter "d" instead of two), it has been corrected to **address**.
1437+- Bugs Fixes.
1438+
1439+### Version 0.3 ( 5 August 2006 )
1440+
1441+- Header support ( client and server side )
1442+- Server side service extensions
1443+- Attribute serialization
1444+- New transport implementation : Synapse HTTP client support
1445+- Address per operation support ( client side )
1446+- Extended meta data
1447+- Bug Fixes
1448+
1449+### Version 0.2.3.2 ( 5 July 2006 )
1450+
1451+- ICS and Indy HTTP Proxy "user" and "passeword" support.
1452+
1453+### Version 0.2.3 ( 4 July 2006 )
1454+
1455+- WSDL generation
1456+- Metadata service
1457+- Metadata Browser sample
1458+- HTTP server sample
1459+- Pascal basic types array implementation
1460+- The ws_helper's parser now supports:
1461+ - {} comment style in the input file
1462+ - service interfaces may have GUID
1463+- more test cases
1464+- bug fix
1465+
1466+### Version 0.2.2 ( 7 June 2006 )
1467+
1468+- All pascal basic types are supported by the **SOAP serializer** and the **Binary serializer** ( Available integers, available floats, string, boolean, Enumerations, class intances )
1469+- Array support for Binary serializer
1470+- FPCUnit test cases
1471+ - SOAP serializer ( basic types and classes instances )
1472+ - Binary serializer ( basic types and classes instances )
1473+- All interfaces now have GUID
1474+
1475+## Author
1476+
1477+Inoussa OUEDRAOGO, <http://inoussa12.googlepages.com/>
1478+
1479+---
1480+*From the Free Pascal wiki, page "Web Service Toolkit/pt", as it stood on 2023-02-06 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
1481+Category: Pages for deletion

Back to history