Lazarus中文社区

 找回密码
 立即注册(注册审核可向QQ群索取)

QQ登录

只需一步,快速开始

Lazarus IDE and 组件 下载地址版权申明
查看: 12017|回复: 0

web services toolkit 0.5 for Lazarus 包括组件下载 范例 教程

[复制链接]

该用户从未签到

发表于 2011-5-27 09:07:52 | 显示全部楼层 |阅读模式
"Web Service Toolkit”是在free pascal 下运用的web services服务包, 可以在Lazarus 和 Delphi 下使用; 可以使用“Web Service Toolkit” 在Lazarus和Delphi下创建Web Service的应用。

直接使用网盘下载: Web Service Toolkit-0.5.zip
  

---------------------------------------------------------------------------------
下面是介绍:

客户端 ( service consumption )

SVN version available for download at: http://groups.google.com/group/wst-list
(The author's site[1] displays versions up until 0.5. But that version doesn't compile with any recent Lazarus version).

概述
“Web Service Toolkit” is made of two parts :
a set of programs : “typ_lib_edtr” a WSDL based type library editor, a command line tool “ws_helper” and a Lazarus integration package which contains some wizards,
a collection of runtime units.
Given an interface definition file(a WSDL file or a pascal file describing a web service), “ws_helper” (or within Lazarus, the WSDL file importer wizard) will create a object pascal unit containing a proxy implementing that interface. At runtime when a call targeting the web service is issued, the proxy's role is to :
marshall the call paramaters,
make the call to the target web service,
receive the call return and unmarshall output parameters to the caller.
Behind the scene, the proxy will take care of the SOAP plumbing details.

范例
We will use the “Amazon E-Commerce Service”, freely available (you will need to create a account, it's free) for personal use at this address [2].In order to use this service, we have to download its exposed WSDL (Web Services Description Language) interface and translate it to Pascal language. To that end, within Lazarus we can use the import wizard; we can also use ws_helper as a standalone program.
[edit] The “WSDL importer” wizard
This wizard is contained in the wst_design.lpk package located in the \ide\lazarus directory of the toolkit. Once the package installed, it adds a menu section “Web Services Toolkit” with two (2) sub-menus items to the Lazarus “Project” menu :
Import WSDL file ...
Type Library Editor ...


Click on the first menu item (Import WSDL file ...), it will bring up the dialog below. We specify the WSDL file and the directory where to store the generated files and click on OK button to finish.


It will produce two (2) pascal source files :
AWSECommerceService.pas, the service definition file (Pascal equivalent of the WSDL file)
AWSECommerceService_proxy.pas, this file contains a proxy which implements the service interface defined in the first file.
[edit] Import by ws_helper.
The ws_helper program is a command line version of the import wizard. In order to present its capabilities, below are printed the command line arguments it supports.
  1.   ws_helper [-uMODE] [-p] [-b] [-i] [-oPATH] inputFilename
  2.     -u MODE Generate the pascal translation of the WSDL input file
  3.        MODE value may be U for used types or A for all types
  4.     -p  Generate service proxy
  5.     -b  Generate service binder
  6.     -i  Generate service minimal implementation
  7.     -o  PATH  Relative output directory
  8.     -a  PATH  Absolute output directory
复制代码

To translate the WDSL file downloaded from the “Amazon E-Commerce Service” web site execute the following command at the prompt:
  1.   ..\..\ws_helper\ws_helper.exe -uA -p -o. AWSECommerceService.wsdl
  2.   ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
  3.   Parsing the file : AWSECommerceService.wsdl
  4.   (...)
  5.   Interface file generation...
  6.   Proxy file generation...
  7.   Metadata file generation...
  8.   File "AWSECommerceService.wsdl" parsed succesfully.
复制代码

below is printed an extract of both files :
  1. unit AWSECommerceService;
  2. {$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
  3. interface
  4. uses SysUtils, Classes, TypInfo, base_service_intf, service_intf;
  5. const
  6.   sNAME_SPACE = 'http://webservices.amazon.com/AWSECommerceService/2007-04-04';
  7.   sUNIT_NAME = 'AWSECommerceService';
  8. type
  9.   HelpRequest = class;
  10.   Help_RequestArray = class;
  11. (...)
  12.   AWSECommerceServicePortType = interface(IInvokable)
  13.     ['{305A7E48-DD92-4C20-B699-4F2B47C93342}']
  14.     function Help(
  15.       Const HelpParam : Help_Type
  16.     ):HelpResponse_Type;
  17.     function ItemSearch(
  18.       Const ItemSearchParam : ItemSearch_Type
  19.     ):ItemSearchResponse_Type;
  20.     function ItemLookup(
  21.       Const ItemLookupParam : ItemLookup_Type
  22.     ):ItemLookupResponse_Type;
  23.     (...)
  24.   end;
  25.   procedure Register_AWSECommerceService_ServiceMetadata();
复制代码
  1. Unit AWSECommerceService_proxy;
  2. {$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
  3. Interface
  4. Uses SysUtils, Classes, TypInfo, base_service_intf, service_intf, AWSECommerceService;
  5. Type
  6.   TAWSECommerceServicePortType_Proxy=class(TBaseProxy,AWSECommerceServicePortType)
  7.   Protected
  8.     class function GetServiceType() : PTypeInfo;override;
  9.     function Help(
  10.       Const HelpParam : Help_Type
  11.     ):HelpResponse_Type;
  12.     function ItemSearch(
  13.       Const ItemSearchParam : ItemSearch_Type
  14.     ):ItemSearchResponse_Type;
  15.     function ItemLookup(
  16.       Const ItemLookupParam : ItemLookup_Type
  17.     ):ItemLookupResponse_Type;   
  18.     (...)
  19.   End;
  20.   Function wst_CreateInstance_AWSECommerceServicePortType(
  21.     const AFormat : string = 'SOAP:';
  22.     const ATransport : string = 'HTTP:'
  23.   ):AWSECommerceServicePortType;
  24. Implementation
  25. uses wst_resources_imp, metadata_repository;
  26. Function wst_CreateInstance_AWSECommerceServicePortType(
  27.   const AFormat : string;
  28.   const ATransport : string
  29. ):AWSECommerceServicePortType;
  30. Begin
  31.   Result := TAWSECommerceServicePortType_Proxy.Create(
  32.     'AWSECommerceServicePortType',
  33.      AFormat +
  34.        GetServiceDefaultFormatProperties(TypeInfo(AWSECommerceServicePortType)),
  35.      ATransport + 'address=' + GetServiceDefaultAddress(TypeInfo(AWSECommerceServicePortType))
  36.   );
  37. End;
  38. function TAWSECommerceServicePortType_Proxy.ItemSearch(
  39.   Const ItemSearchParam : ItemSearch_Type
  40. ):ItemSearchResponse_Type;
  41. Var
  42.   locSerializer : IFormatterClient;
  43.   strPrmName : string;
  44. Begin
  45.   locSerializer := GetSerializer();
  46.   Try
  47.     locSerializer.BeginCall('ItemSearch', GetTarget(),(Self as ICallContext));
  48.       locSerializer.Put('ItemSearch', TypeInfo(ItemSearch_Type), ItemSearchParam);
  49.     locSerializer.EndCall();
  50.     MakeCall();
  51.     locSerializer.BeginCallRead((Self as ICallContext));
  52.       TObject(Result) := Nil;
  53.       strPrmName := 'ItemSearchResponse';
  54.       locSerializer.Get(TypeInfo(ItemSearchResponse_Type), strPrmName, Result);
  55.   Finally
  56.     locSerializer.Clear();
  57.   End;
  58. End;
  59. (...)
复制代码

We are now able to build a simple program for the service. The Synapse Library is required to compile the program as it is used for the HTTP communication. This library can be downloaded free of charge at this location[3]. The Indy[4] Library or ICS[5] library can also be used.
  1. program amazon_sample;
  2. {$mode objfpc}{$H+}
  3. uses
  4.   Classes, SysUtils, soap_formatter, synapse_http_protocol,
  5.   metadata_repository, AWSECommerceService, AWSECommerceService_proxy;
  6. const sACCES_ID = <Your AccesID here>;
  7. function ReadEntry(const APromp : string):string ;
  8. begin
  9.   Result := '';
  10.   Write(APromp);
  11.   while True do begin
  12.     ReadLn(Result);
  13.     Result := Trim(Result);
  14.     if ( Length(Result) > 0 ) then
  15.       Break;
  16.   end;
  17. end;
  18. var
  19.   locService : AWSECommerceServicePortType;
  20.   rqst : ItemSearch_Type;
  21.   rsps : ItemSearchResponse_Type;
  22.   rspsItem : Items_Type;
  23.   i, j, k : Integer;
  24.   itm : Item_Type;
  25. begin
  26.   SYNAPSE_RegisterHTTP_Transport();
  27.   WriteLn('Web Services Toolkit Amazon sample');
  28.   WriteLn('This sample demonstrates the "ItemSearch" method of the Amazon web service');
  29.   WriteLn();
  30.   rqst := ItemSearch_Type.Create();
  31.   try
  32.     locService := wst_CreateInstance_AWSECommerceServicePortType();
  33.     rqst.AWSAccessKeyId := sACCES_ID;
  34.     while True do begin
  35.       rqst.Request.SetLength(1);
  36.       rqst.Request[0].SearchIndex := ReadEntry('Enter the Search Index : ');
  37.       rqst.Request[0].Availability := Available;
  38.       rqst.Request[0].Count := 10;
  39.       rqst.Request[0].MerchantId := 'Amazon';
  40.       rqst.Request[0].ItemPage := 1;
  41.       rqst.Request[0].Keywords := ReadEntry('Enter the Keywords : ');
  42.       rsps := locService.ItemSearch(rqst);
  43.       if ( rsps.OperationRequest.Errors.Length > 0 ) then begin
  44.         WriteLn(Format('Errors ( %d ) : ',[rsps.OperationRequest.Errors.Length]));
  45.         for i := 0 to Pred(rsps.OperationRequest.Errors.Length) do begin
  46.           WriteLn(Format('  Error[%d] :',[i]));
  47.           WriteLn('    ' + rsps.OperationRequest.Errors[i].Code);
  48.           WriteLn('    ' + rsps.OperationRequest.Errors[i].Message);
  49.         end;
  50.       end else begin
  51.         WriteLn(Format('Response ( %d ) : ',[rsps.Items.Length]));
  52.         if Assigned(rsps) then begin
  53.           for i := 0 to Pred(rsps.Items.Length) do begin
  54.             rspsItem := rsps.Items[i];
  55.             WriteLn('    TotalPages :' + IntToStr(rspsItem.TotalPages));
  56.             WriteLn('    TotalResults :' + IntToStr(rspsItem.TotalResults));
  57.             WriteLn('    Items :' + IntToStr(rspsItem._Item.Length));
  58.             WriteLn('');
  59.             for j := 0 to Pred(rspsItem._Item.Length) do begin
  60.               itm := rspsItem._Item[j];;
  61.               WriteLn('        ASIN :' + itm.ASIN);
  62.               WriteLn('        DetailPageURL :' + itm.DetailPageURL);
  63.               if Assigned(itm.ItemAttributes) then begin
  64.                 WriteLn('               Title :' + itm.ItemAttributes.Title);
  65.                 for k := 0 to Pred(itm.ItemAttributes.Author.Length) do begin
  66.                   WriteLn('               Author[ ' + IntToStr(k) + ' ] ' + itm.ItemAttributes.Author.Item[k]);
  67.                 end;
  68.                 WriteLn('               Manufacturer :' + itm.ItemAttributes.Manufacturer);
  69.                 WriteLn('               ProductGroup :' + itm.ItemAttributes.ProductGroup);
  70.               end;
  71.               WriteLn('');
  72.             end;
  73.           end;
  74.         end else begin
  75.           WriteLn('Unexpected service response : Invalid response');
  76.         end;
  77.       end;
  78.       WriteLn();
  79.       WriteLn();
  80.       if ( UpperCase(ReadEntry('Continue ( Y/N ) :'))[1] <> 'Y' ) then
  81.         Break;
  82.     end;
  83.   finally
  84.     FreeAndNil(rqst);
  85.     FreeAndNil(rsps);
  86.   end;
  87.   ReadLn;
  88. end.
复制代码

The units base_service_intf, service_intf, soap_formatter, synapse_http_protocol, wst_resources_imp and metadata_repository are provided with the toolkit; Below is the result of a execution session searching for “Freepascal” in the “All” search index ( the service is case-sensitive!) .
  1. Web Services Toolkit Amazon sample
  2. This sample demonstrates the "ItemSearch" method of the Amazon web service
  3. Enter the Search Index : All
  4. Enter the Keywords : Freepascal
  5. Response ( 1 ) :
  6.     TotalPages :1
  7.     TotalResults :9
  8.     Items :9
  9.         ASIN :0470088702
  10.         DetailPageURL : http://www.amazon.com/gp/redirect.html%3FASIN=0470088702%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0470088702%253FSubscriptionId=0W3H25JMMGBNBXSTQN82
  11.                Title :Beginning Programming For Dummies (Beginning Programming f
  12. or Dummies)
  13.                Author[ 0 ] Wallace Wang
  14.                Manufacturer :For Dummies
  15.                ProductGroup :Book
  16.         ASIN :0471375233
  17.         DetailPageURL : http://www.amazon.com/gp/redirect.html%3FASIN=0471375233%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/0471375233%253FSubscriptionId=0W3H25JMMGBNBXSTQN82
  18.                Title :Assembly Language Step-by-step: Programming with DOS and Linux (with CD-ROM)
  19.                Author[ 0 ] Jeff Duntemann
  20.                Manufacturer :Wiley
  21.                ProductGroup :Book
  22. (...)
复制代码

It finds one (1) page containing nine (9) items! The wst_CreateInstance_AWSECommerceServicePortType() function, located in the AWSECommerceService.pas file create a proxy instance based on the service's informations contained in the WSDL file.
  1. Function wst_CreateInstance_AWSECommerceServicePortType(
  2.   const AFormat    : string;
  3.   const ATransport : string
  4. ):AWSECommerceServicePortType;
  5. Begin
  6.   Result := TAWSECommerceServicePortType_Proxy.Create(
  7.               'AWSECommerceServicePortType',
  8.               Aformat + GetServiceDefaultFormatProperties(TypeInfo(AWSECommerceServicePortType)),
  9.               ATransport + 'address=' + GetServiceDefaultAddress(TypeInfo(AWSECommerceServicePortType))
  10.             );
  11. End;
复制代码

The complete source code of the amazon example is located in the \samples\amazon folder for fpc; The Delphi sample is located in the \samples\delphi\amazon folder.

连接参数:
The general format is:
  1.   protocol:paramName=paramValue(;paramName=paramValue)*
复制代码
[edit] HTTP Proxy Parameters
For HTTP the supported parameters are:
Address ( required for a service supporting a unique address )
ProxyServer
ProxyPort
ProxyUsername
ProxyPassword
HTTP Connection through proxy are supported. Below is an example address string.

  const
    sADDRESS = 'http:address=http://webservices.amazon.com/AWSECommerceService/2007-04-04'+
                 'roxyServer=197.150.10.10roxyPort=9881'+
                 'roxyUsername=inoussaroxyPassword=wst';

The toolkit has two TCP implementations based on Synapse ( synapse_tcp_protocol.pas ) and ICS ( ics_tcp_protocol.pas ).
[edit] TCP connection Parameters
TCP supported parameters are:
address ( required for a service supporting a unique address )
Port
target( the target service )
Below is an example address string.

  Const
    sADDRESS = 'TCP:Address=10.0.0.3ort=1234;target=UserService';

The toolkit has two TCP implementations based on Synapse ( synapse_tcp_protocol.pas ) and ICS ( ics_tcp_protocol.pas ).
[edit] LIBRARY ( LIB ) connection Parameters
The idea behind this protocol is to be able to host services in dynamic libraries ( DLL/DSO ). It can be viewed as a plug-in framework where plug-ins ( services ) are provided by dynamic libraries. LIB supported parameters are:
FileName ( the DLL/SO filename )
target( the target service )
Below is an example address string.

  Const
    sADDRESS = 'LIB:FileName=..\library_server\lib_server.dll;target=UserService';

The toolkit has one LIB implementation ( library_protocol.pas ).
The samples folder contains 4 projects user_client_console, tcp_server, http_server and library_server which demonstrate the TCP, HTTP and LIBRARY protocols.
[edit] Multi-Address service ( Address per operation )
Certain services ( like the eBay SOAP services ) use a address per operation. The toolkit uses extended meta data ( see the services meta data chapter below ) to set operation's addresses. The “ebay” SOAP sample located under the tests\ebay folder demonstrates the operation's address setting.
[edit] Troubleshooting
[edit] 'Invalid parameter : "AProtocolData"' error message
This happens when it is not clear wich protocol to use. Make sure that a valid formatter is selected (for example: 'SOAP:') and that the corresponding formatter is registered. This is done by adding the appropiate xxx_formatter unit to your uses-clausule. (for example: soap_formatter)
[edit] Server Side ( service creation )

[edit] Overview
Web Service Toolkit contains a server side framework and a WSDL based type library editor for service creation. Key features are:
Service definition ( interface ) is separated from implementation,
Interface and implementations are not bound to message protocol,
WSDL generation
Support for SOAP 1.1 serialization
Support for XMLRPC serialization
Support for custom binary serialization
The framework is not bound to a transport protocol
Easy to add support for application servers.
[edit] Example
In order to create a service, we have to :
define its interface,
provide an implementation and register that one for the service,
provide a binder that will route calls targeting the service to the implementation and register that one,
host the service into an application server( TCP server, HTTP Server, LIBRARY server, ... ).
[edit] Defining the service Interface
Starting from the 0.5 version, the WST provides a WSDL based Type library Editor to define types and services used by an implementation. The figure (3) below presents the general interface of this tool. The Type library Editor is provided as
a Lazarus wizard as a menu item “Project/Web Services Toolkit/Type Library Editor..”
a standalone program typ_lib_edtr.exe.


Type Library Editor features:
Graphical user interface
WSDL source view
Pascal source view of the library
Pascal Proxy source view
Pascal implementation skeleton source view
Pascal Proxy binder view
enumeration creation interface
class creation interface
array creation interface
type alias creation interface
service interface creation interface.
Below are showed some sample images. We will use the user_service_intf.wsdl file located in the \samples directory for our sample.


[edit] Export the pascal files.
The Type Library Editor has option to generate the pascal version of the WSDL file. To do so, click on the “Files\Save generated files ...” ( it can also be done through the contextual menu ); It will bring up the dialog box showed in the following image.  
Click OK the button to complete. The program ws_helper has the same capabilities and the files can be generated with the following command :
ws_helper\ws_helper.exe -i -b -o. user_service_intf.wsdl
ws_helper, Web Service Toolkit 0.5 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
Parsing the file : user_service_intf.wsdl
Information : Parsing "tns:TUserArray" ...
Information : Parsing "tns:TUser" ...
Information : Parsing "tns:TUser" ...
Information : Parsing "xsd:string" ...
Information : Parsing "tns:TUser" ...
Information : Parsing "xsd:string" ...
Information : Parsing "xsd:boolean" ...
Information : Parsing "TUserArray" ...
Information : Parsing "TUser" ...
Information : Parsing "TUserCategory" ...
Information : Parsing "TNote" ...
Interface file generation...
Proxy file generation...
Metadata file generation...
File "user_service_intf.wsdl" parsed succesfully.
The complete projects of the example is located in the folder “samples”. Below is printed a extract of the generated interface file.

unit user_service_intf;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
interface

uses SysUtils, Classes, TypInfo, base_service_intf, service_intf;

const
  sNAME_SPACE = 'urn:UserService';
  sUNIT_NAME = 'user_service_intf';

type

  TUser = class;
  TUserArray = class;

  TUserCategory = (
    Normal
    ,Admin
  );

  TUser = class(TBaseComplexRemotable)
  published
    property Category : TUserCategory read FCategory write FCategory;
    property UserName : string read FUserName write FUserName;
    property eMail : string read FeMail write FeMail;
    property Preferences : string read FPreferences write FPreferences;
    property Note : TNote read FNote write FNote;
  end;

  TUserArray = class(TBaseObjectArrayRemotable)
  private
    function GetItem(AIndex: Integer): TUser;
  public
    class function GetItemClass():TBaseRemotableClass;override;
    property Item[AIndex:Integer] : TUser Read GetItem;Default;
  end;

  UserService = interface(IInvokable)
    ['{CA6F6192-C3DE-4D9C-B3DF-E616376A0DC9}']
    function GetList():TUserArray;
    procedure Add(
      Const AUser : TUser
    );
    procedure Update(
      Const AUser : TUser
    );
    function Find(
      Const AName : string
    ):TUser;
    function Delete(
      Const AName : string
    ):boolean;
  end;
(...)

[edit] Providing an implementation for the service
The user_service_intf_imp.pas unit generated above 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.

Unit user_service_intf_imp;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
Interface
Uses SysUtils, Classes,
     base_service_intf, server_service_intf, server_service_imputils,
     user_service_intf, cursor_intf;

Type
  { TUserService_ServiceImp }

  TUserService_ServiceImp=class(TBaseServiceImplementation,UserService)
 &#160rotected
    function GetList():TUserArray;
    procedure Add(
      Const AUser : TUser
    );
    procedure Update(
      Const AUser : TUser
    );
    function Find(
      Const AName : string
    ):TUser;
    function Delete(
      Const AName : string
    ):boolean;
  End;

  procedure RegisterUserServiceImplementationFactory();

Implementation
(...)

procedure TUserService_ServiceImp.Add(Const AUser : TUser);
var
  locObj : TUser;
Begin
  locObj := Find(AUser.UserName);
  if ( locObj <> nil ) then
    raise Exception.CreateFmt('Duplicated user : "%s"',[AUser.UserName]);
  locObj := TUser.Create();
  locObj.Assign(AUser);
  FUserList.Add(locObj);
End;

procedure RegisterUserServiceImplementationFactory();
Begin
  GetServiceImplementationRegistry().Register(
    'UserService',
TImplementationFactory.Create(TUserService_ServiceImp) as IServiceImplementationFactory);
End;

(...)

“ws_helper” has options to generate proxy file, basic implementation skeleton file and a binder file ( see the following listing).
ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
ws_helper [-uMODE] [-p] [-b] [-i] [-oPATH] inputFilename
  -u MODE Generate the pascal translation of the WSDL input file
       MODE value may be U for used types or A for all types
  -p  Generate service proxy
  -b  Generate service binder
  -i  Generate service minimal implementation
  -o &#160ATH  Relative output directory
  -a &#160ATH  Absolute output directory
The starting implementation file could also be create using ws_helper with the -i and -b options as above;
ws_helper\ws_helper.exe -i -b -o. user_service_intf.wsdl
ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
Parsing the file : user_service_intf.wsdl
Proxy file generation...
Binder file generation...
Implementation file generation...
Metadata file generation...
File "user_service_intf.wsdl" parsed succesfully..
[edit] Providing a binder for the service.
The binder's role is to:
unpack the incoming message,
set up the call stack,
make the call against the registered implementation,
serialize the execution stack to create the return message.
The user_service_intf_binder.pas unit generated above, contains :
TUserService_ServiceBinder : the actual binder class,
TUserService_ServiceBinderFactory a factory class for the binder and
Server_service_RegisterUserServiceService : the binder factory registration procedure.
The following code extract shows the unit interface part and a method handler of the binder.

unit user_service_intf_binder;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
interface
uses SysUtils, Classes, base_service_intf, server_service_intf, user_service_intf;

type
  TUserService_ServiceBinder=class(TBaseServiceBinder)
 &#160rotected
    procedure GetListHandler(AFormatter:IFormatterResponse);
    procedure AddHandler(AFormatter:IFormatterResponse);
    procedure UpdateHandler(AFormatter:IFormatterResponse);
    procedure FindHandler(AFormatter:IFormatterResponse);
    procedure DeleteHandler(AFormatter:IFormatterResponse);
 &#160ublic
    constructor Create();
  End;

  TUserService_ServiceBinderFactory = class(TInterfacedObject,IItemFactory)
  protected
    function CreateInstance():IInterface;
  End;

  procedure Server_service_RegisterUserServiceService();

(...)

procedure TUserService_ServiceBinder.AddHandler(AFormatter:IFormatterResponse);
Var
  cllCntrl : ICallControl;
  tmpObj : UserService;
  callCtx : ICallContext;
  strPrmName : string;
  procName,trgName : string;
  AUser : TUser;
Begin
  callCtx := GetCallContext();
  TObject(AUser) := Nil;

  strPrmName := 'AUser';  AFormatter.Get(TypeInfo(TUser),strPrmName,AUser);
  If Assigned(Pointer(AUser)) Then
    callCtx.AddObjectToFree(TObject(AUser));

  tmpObj := Self.GetFactory().CreateInstance() as UserService;
  if Supports(tmpObj,ICallControl,cllCntrl) then
    cllCntrl.SetCallContext(GetCallContext());

  tmpObj.Add(AUser);

  procName := AFormatter.GetCallProcedureName();
  trgName := AFormatter.GetCallTarget();
  AFormatter.Clear();
  AFormatter.BeginCallResponse(procName,trgName);
  AFormatter.EndCallResponse();

  callCtx := Nil;
End;

[edit] Host the service into an application server.
The application server's role is to route incoming service requests to the Web Service Toolkit runtime. For the runtime to process service requests :
The services and their implementations have to be registered ,
The message protocol (SOAP, binary,...) have to be registered.
The runtime interface is defined in the server_service_intf unit. This unit contains :
GetServerServiceRegistry, which returns the service registry,
GetServiceImplementationRegistry which returns the service implementation registry,
GetFormatterRegistry which returns the message format registry and
HandleServiceRequest which is the unique entry point for request processing.
Starting from the version 0.5, the toolkit provides a simplified model to develop applications server . This is achieved using the listener classes. A listener implements a transport between the server and its clients. The toolkit provides three (3) listeners implementations:
TwstIndyHttpListener ( indy_http_server.pas ),
TwstIndyTcpListener ( indy_tcp_server.pas ) and
TwstSynapseTcpListener ( synapse_tcp_server.pas ).
All listeners derive from TwstListener defined in the server_listener.pas file.
Below is printed a Indy based HTTP server sample. The code is divided into three (3) parts :
messaging format registration :

  Server_service_RegisterSoapFormat();
  Server_service_RegisterXmlRpcFormat();
  Server_service_RegisterBinaryFormat() ;

service implementation and binder registration :

  RegisterUserServiceImplementationFactory();
  Server_service_RegisterUserServiceService();

the listner creation and starting : the listener is created and started by the lines

  AppObject  := TwstIndyHttpListener.Create();
  AppObject.Start();


Complete listing :

program http_server;
{$mode objfpc}{$H+}
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, SysUtils,
  indy_http_server, metadata_service, logger_extension, server_listener,
  server_service_soap, server_binary_formatter, server_service_xmlrpc, config_objects,
  user_service_intf, user_service_intf_binder, user_service_intf_imp;
var
  AppObject : TwstListener;
begin
  Server_service_RegisterBinaryFormat();
  Server_service_RegisterSoapFormat();
  Server_service_RegisterXmlRpcFormat();

  RegisterUserServiceImplementationFactory();
  Server_service_RegisterUserServiceService();
  AppObject := TwstIndyHttpListener.Create();
  try
    WriteLn('"Web Service Toolkit" HTTP Server sample listening at:');
    WriteLn('');
    WriteLn('http://127.0.0.1:8000/');
    WriteLn('');
    WriteLn('Press enter to quit.');
    AppObject.Start();
    ReadLn();
  finally
    FreeAndNil(AppObject);
  end;
end.  

Server_service_RegisterUserServiceService located in the user_service_intf_binder unit ( generated by ws_helper ) registers the UserService service by calling in turn GetServerServiceRegistry:

procedure Server_service_RegisterUserServiceService();
Begin
  GetServerServiceRegistry().Register(
    'UserService',TUserService_ServiceBinderFactory.Create() as IitemFactory
  );
End;

Server_service_RegisterSoapFormat located in the server_service_soap unit ( provided by the toolkit ) registers the SOAP implementation by calling in turn GetFormatterRegistry:

  procedure Server_service_RegisterSoapFormat();
  begin
    GetFormatterRegistry().Register(
      sPROTOCOL_NAME,
      sSOAP_CONTENT_TYPE,
      TSimpleItemFactory.Create(TSOAPFormatter) as IitemFactory
    );
    RegisterStdTypes();
  end;

Server_service_RegisterBinaryFormat located in the server_binary_formatter unit ( provided by the toolkit ) registers the Binary message implementation by calling in turn GetFormatterRegistry:

  procedure Server_service_RegisterBinaryFormat();
  begin
    GetFormatterRegistry().Register(
      sPROTOCOL_NAME,
      sBINARY_CONTENT_TYPE,
      TBinaryFormatterFactory.Create() as IitemFactory
    );
  end;

Server_service_RegisterXmlRpcFormat located in the server_service_xmlrpc unit ( provided by the toolkit ) registers the XMLRPC message implementation by calling in turn GetFormatterRegistry:

  procedure Server_service_RegisterXmlRpcFormat();
  begin
    GetFormatterRegistry().Register(
      sPROTOCOL_NAME,
      sXMLRPC_CONTENT_TYPE,
      TSimpleItemFactory.Create(TXmlRpcFormatter) as IItemFactory
    );
  end;

In order to give it a try one have to :
compile the server ( \samples\tcp_server\tcp_server.lpi it is a console program),
compile the client application ( \samples\user_client_console\user_client_console.lpi ),
execute the server and start listening,
execute the client.
[edit] WSDL generation
Services in the toolkit are organized into meta data repositories. Conceptually a repository corresponds :
at compile time to the pascal unit containing the service definition
at runtime to a name space.
The repository is the toolkit WSDL generation unit.

[edit] The Metadata Service
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.
[edit] WSDL generation API
The metadata_wsdl pascal unit contains the GenerateWSDL function for WSDL generation from a repository (see the signature below).

  PServiceRepository = ^TServiceRepository;
  TServiceRepository = record
    NameSpace        : ShortString;
    Name             : ShortString;
    RootAddress      : ShortString;
    ServicesCount    : Byte;
    Services         : PService;
  end;

  procedure GenerateWSDL(AMdtdRep : PServiceRepository; ADoc : TDOMDocument);

[edit] WSDL Customization
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.
[edit] Sample
A functional sample project is located under \samples\http_server . It is an Indy base http server.
[edit] Services Extensions
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.

  TMessageStage = (
    msAfterDeserialize, msAfterSerialize, msBeforeDeserialize, msBeforeSerialize
  );

  IServiceExtension = interface
    ['{E192E6B3-7932-4D44-A8AC-135D7A0B8C93}']
    procedure ProcessMessage(
      const AMessageStage     : TMessageStage;
               ACallContext   : ICallContext;
               AMsgData       : IInterface
    );
  end;

The AMsgData parameter actual type depends on the message processing state and corresponds to :
IRequestBuffer on "msBeforeDeserialize" and "msAfterSerialize"
IFormatterResponse on "msAfterDeserialize" and "msBeforeSerialize"
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

  IServiceExtensionRegistry = Interface
    ['{68DC78F1-E6CF-4D6B-8473-75288794769C}']
    function Find(const AName : string):IServiceExtension;
    procedure Register(
      const AName    : string;
            AFactory : IItemFactory
    );
  end;

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 ).
[edit] Services meta data

Services in the toolkit are organized into meta data repositories( see the “services's meta data” below ). Conceptually a repository corresponds :
at compile time to the pascal unit containing the service definition
at runtime to a name space.
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.

  IModuleMetadataMngr = interface
    ['{B10ACF6A-A599-45A3-B083-BEEFB810C889}']
    function IndexOfName(const ARepName : shortstring):Integer;
    function GetCount():Integer;
    function GetRepositoryName(const AIndex : Integer):shortstring;
    procedure SetRepositoryNameSpace(const ARepName,ANameSpace : shortstring);
    function LoadRepositoryName(
      const ARepName,ARootAddress  : shortstring;
      out   ARepository  : PServiceRepository
    ):Integer;
    procedure ClearRepository(var ARepository : PServiceRepository);
    procedure SetServiceCustomData(
      const ARepName       : shortstring;
      const AServiceName   : shortstring;
      const ADataName,
            AData          : string
    );
    procedure SetOperationCustomData(
      const ARepName       : shortstring;
      const AServiceName   : shortstring;
      const AOperationName : shortstring;
      const ADataName,
            AData          : string
    );
    function GetServiceMetadata(const ARepName,AServiceName : shortstring) : PService;
    procedure ClearServiceMetadata(var AService : PService);
  end;

[edit] Extended Meta data
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 .

  initialization
    {$i ebay.lrs}

    {$IF DECLARED(Register_ebay_ServiceMetadata)}
    Register_ebay_ServiceMetadata();
    {$ENDIF}
  End.  

[edit] Headers support

[edit] The THeaderBlock class

  THeaderBlock = class(TBaseComplexRemotable)
  public
    property Direction : THeaderDirection read FDirection write FDirection;
    property Understood : Boolean read FUnderstood write FUnderstood;
  published
    property mustUnderstand : Integer read FmustUnderstand write SetmustUnderstand
      stored HasmustUnderstand;
  end;

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.
[edit] Defining header class
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.

  TCalcHeader = class(THeaderBlock)
  published
    property Login : string read FLogin write FLogin;
    property Password : string read FPassword write FPassword;
    property WantedPrecision : Integer read FWantedPrecision write FWantedPrecision;
  end;

[edit] The ICallContext interface

  ICallContext = Interface
    ['{855EB8E2-0700-45B1-B852-2101023200E0}']
    procedure AddObjectToFree(const AObject : TObject);
    procedure Clear();
    function AddHeader(
      const AHeader        : THeaderBlock;
      const AKeepOwnership : Boolean
    ):Integer;
    function GetHeaderCount(const ADirections : THeaderDirections):Integer;
    function GetHeader(const AIndex : Integer) : THeaderBlock;
    procedure ClearHeaders(const ADirection : THeaderDirection);
  End;

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.
[edit] Client side headers
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.

  var
    ch : TCalcHeader;
    hdrs : ICallContext;
  begin
    FObj := TCalculator_Proxy.Create('Calculator', edtFormat.Text, edtAddress.Text);

    ch := TCalcHeader.Create();
    ch.mustUnderstand := 1;
    ch.Login := 'azerty';
    ch.Password := 'qwerty';
    ch.WantedPrecision := 1210;
    hdrs := FObj as ICallContext;
    hdrs.AddHeader(ch,true);

A header may be made mandatory by setting its mustUnderstand property to 1 as in the code above.
[edit] Server side headers
[edit] The ICallControl interface

  ICallControl = interface
    ['{7B4B7192-EE96-4B52-92C7-AE855FBC31E7}']
    procedure SetCallContext(ACallContext : ICallContext);
    function GetCallContext():ICallContext;
  end;

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.

  function TCalculator_ServiceImp.AddInt(
    Const A : Integer;
    Const B : Integer
  ):TBinaryArgsResult;
  var
    hdr : TCalcResultHeader;
    h : TCalcHeader;
    cc : ICallContext;
  Begin
    hdr := TCalcResultHeader.Create();
    cc := GetCallContext();
    if Assigned(cc) and ( cc.GetHeaderCount([hdIn]) > 0 ) and ( cc.GetHeader(0).InheritsFrom(TCalcHeader) ) then begin
      h := cc.GetHeader(0) as TCalcHeader;
      h.Understood := True;
      hdr.Assign(h);
    end;
    hdr.TimeStamp := DateTimeToStr(Now());
    hdr.SessionID := 'testSession';
    cc.AddHeader(hdr,True);
    hdr := nil;
    Result := TBinaryArgsResult.Create();
    Try
      Result.Arg_OP := '+';
      Result.Arg_OpEnum := coAdd;
      Result.Arg_A := A;
      Result.Arg_B := B;
      Result.Arg_R := A + B;
      Result.Comment := 'Doing an + operation';
    Except
      FreeAndNil(Result);
      Raise;
    End;
  End;

[edit] SOAP Specific

[edit] Binding style
The binding style is used to indicate whether the service is RPC oriented or Document oriented.
[edit] Client side
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.

  locService := TSampleService_Proxy.Create(
                  'SampleService',
                  'SOAP:Style=Document;EncodingStyle=Litteral',
                  'http:Address=http://127.0.0.1/services/SampleService'
                );

[edit] Server side
Currently services created with the toolkit use the RPC binding style.
[edit] Encoding style
The encoding style indicates the rules used to encode types in XML. Supported values are Encoded and Litteral.
[edit] Client side
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.
[edit] Server side
Currently services created with the toolkit use the Encoded encoding style.
[edit] Provided examples

The samples are located under the “tests” folder.
[edit] Client side examples ( tested on Windows XP, Ubuntu and MacOS on PowerPC)
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)
Google sample : It demonstrates use of class and array data types .
Metadata Browser : This sample demonstrates use of class and array data types and mainly the toolkit metadata service.
eBay sample, this sample uses OpenSLL which can be found at http://www.openssl.org and SYNAPSE ( http://www.ararat.cz/synapse/ ).
\samples\delphi\ : This folder contains Delphi ( compile with Delphi 7) client and server samples. Used protocol : the TCP, HTTP, LIBRARY; Used format : SOAP, XMLRPC and BINARY.
[edit] Server side examples
samples\tcp_server : This is a sample TCP server based on the Synapse components. It uses the UserService.
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.
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.
[edit] Status

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.
[edit] Serialization
The serialization is based on the IFormatterBase interface located in the base_service_intf unit.
The toolkit has three serializers implementations : the SOAP serializer, the XMLRPC serializer and a binary serializer. This serializers has been tested on fpc 2.x and Delphi 7.
[edit] SOAP serializer
The SOAP serializer implements SOAP 1.1. It has support for the following pascal types:
Available integers :
Byte mapped to unsignedByte
ShortInt mapped to byte
SmallInt mapped to short
Word mapped to unsignedShort
LongInt mapped to int
LongWord mapped to unsignedInt
Int64 mapped to long
QWord mapped to int
String mapped to string
Boolean mapped to boolean
Enumerations mapped to their string representation
Float types :
Single mapped to float
Double mapped to double
Extended mapped to double
Currency mapped to float
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.
[edit] Binary serializer
The Binary serializer is more efficient in time and space compared to the SOAP serializer . It uses big endian to stream data. It has support for the following pascal types:
Available integers :
Byte
ShortInt
SmallInt
Word
LongInt
LongWord
Int64
QWord
String
Boolean
Enumerations
Float types :
Single
Double
Extended
Currency
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.
[edit] Class type serialization
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.
[edit] The root “TBaseRemotable” class

  TBaseRemotable = class(TPersistent)
  Public
    constructor Create();virtual;
    class procedure Save(
            AObject   : TBaseRemotable;
            AStore    : IFormatterBase;
      Const AName     : String;
      Const ATypeInfo : PTypeInfo
    );virtual;abstract;
    class procedure Load(
      Var   AObject   : TObject;
            AStore    : IFormatterBase;
      var   AName     : String;
      const ATypeInfo : PTypeInfo
    );virtual;abstract;
  End;

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 :
Save : this method is called when the toolkit needs to serialize the AObject parameter.
Load: this method is called when the toolkit needs to un-serialize to the AObject parameter.
[edit] The “TBaseComplexRemotable” serialization

  TBaseComplexRemotable = class(TAbstractComplexRemotable)
  public
    class procedure Save(
            AObject   : TBaseRemotable;
            AStore    : IFormatterBase;
      const AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
    class procedure Load(
      var   AObject   : TObject;
            AStore    : IFormatterBase;
      var   AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
    class procedure RegisterAttributeProperty(const AProperty : shortstring);virtual;
    class procedure RegisterAttributeProperties(const APropertList : array of shortstring);virtual;
    class function IsAttributeProperty(const AProperty : shortstring):Boolean;
    procedure Assign(Source: TPersistent); override;
  end;

TBaseComplexRemotable implements serialization for its descendants classes published properties. The serialization is based on runtime type information (RTTI) and can be customized to:
ignore always some published properties.
ignore conditionally some published properties.
The following class shows a the serialization's customization sample.

  TSampleClass = class(TBaseComplexRemotable)
  private
    FProp_Always: Integer;
    FProp_Never: Integer;
    FProp_Optional: Integer;
    function GetStoredProp_Optional: boolean;
  published
    //This property will always be serialized
    property Prop_Always : Integer read FProp_Always write FProp_Always;
    //This property will never be serialized
    property Prop_Never : Integer read FProp_Never write FProp_Never stored False;
    //This property will be serialized if "Self.GetStoredProp_Optional() = True"
    property Prop_Optional : Integer read FProp_Optional write FProp_Optional stored GetStoredProp_Optional;
  End;

Attribute properties
TBaseComplexRemotable allows properties serialization as attributes. Theses properties have to be registered as such with the RegisterAttributeProperty class method or RegisterAttributeProperties one.

[edit] TBaseComplexRemotable
TBaseComplexSimpleContentRemotable provides implementation for the “XML Schema” complex types which extend simple types with attributes. The following example illustrates this :

  <xs:complexType name="DecimalWithUnits">
    <xs:simpleContent>
      <xs:extension base="xs:decimal">
        <xs:attribute name="Units" type="xs:string"
                      use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

This type will be translate by ws_helper to Pascal as

  DecimalWithUnits = class(TComplexFloatExtendedContentRemotable)
  private
    FUnits : string;
  published
    property Units : string read FUnits write FUnits;
  end;

using the predefined types ( in base_service_intf.pas )

  TBaseComplexSimpleContentRemotable =
     class(TAbstractComplexRemotable)
  protected
    class procedure SaveValue(
      AObject : TBaseRemotable;
      AStore : IformatterBase
    );virtual;abstract;
    class procedure LoadValue(
      var AObject : TObject;
      AStore : IformatterBase
    );virtual;abstract;
  public
    class procedure Save(
            AObject   : TBaseRemotable;
            AStore    : IFormatterBase;
      const AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
    class procedure Load(
      var   AObject   : TObject;
            AStore    : IFormatterBase;
      var   AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
  end;

  TComplexFloatExtendedContentRemotable =
     class(TBaseComplexSimpleContentRemotable)
  private
    FValue: Extended;
  protected
    class procedure SaveValue(
      AObject : TBaseRemotable;
      AStore : IformatterBase
    );override;
    class procedure LoadValue(
       var AObject : TObject;
       AStore : IformatterBase
    );override;
  public
    property Value : Extended read FValue write FValue;
  end;

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, ...).

  <example Units = "meter">
    12.10
  </example>

[edit] Provided array implementations
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.
Available integers :
Byte TArrayOfInt8URemotable
ShortInt TArrayOfInt8SRemotable
SmallInt TArrayOfInt16SRemotable
Word TArrayOfInt16URemotable
LongInt TArrayOfInt32SRemotable
LongWord    TArrayOfInt32URemotable
Int64 TArrayOfInt64SRemotable
Qword TArrayOfInt64URemotable
String TarrayOfStringRemotable( AnsiString )
Boolean TArrayOfBooleanRemotable
Float types :
Single TArrayOfFloatSingleRemotable
Double     TArrayOfFloatDoubleRemotable
Extended TArrayOfFloatExtendedRemotable
Currency TArrayOfFloatCurrencyRemotable
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 ):

  <xs:complexType name="CustomerContentSearchRequest">
    <xs:sequence>
      <xs:element name="CustomerPage" type="xs:positiveInteger"
         minOccurs="0"/>
      <xs:element name="Email" type="xs:string" minOccurs="0"/>
      <xs:element name="Name" type="xs:string" minOccurs="0"/>
      <xs:element name="ResponseGroup" type="xs:string"
           minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

which could be instantiated as

  <search>
    <CustomerPage> 1 </CustomerPage>
    <Name>Sample name</Name>
    <ResponseGroup>Group 1</ResponseGroup>
    <ResponseGroup>Group 2</ResponseGroup>
    <ResponseGroup>Group 3</ResponseGroup>
  </search>

This type will be translate to Pascal by ws_helper as (the private and protected parts are omitted to be short)

(...)
  CustomerContentSearchRequest_ResponseGroupArray =
    class(TBaseSimpleTypeArrayRemotable)
  public
    class function GetItemTypeInfo()TypeInfo;override;
    procedure SetLength(const ANewSize : Integer);override;
    property Item[AIndex:Integer] : string
      read GetItem write SetItem; default;
  end;

  CustomerContentSearchRequest = class(TBaseComplexRemotable)
  published
    property CustomerPage : positiveInteger
       read FCustomerPage
       write FCustomerPage stored HasCustomerPage;
    property Email : string
       read FEmail
       write FEmail
       stored HasEmail;
    property Name : string read FName write FName stored HasName;
    property ResponseGroup :
       CustomerContentSearchRequest_ResponseGroupArray
       read FResponseGroup
       write FResponseGroup;
  end;

implementation
(...)
  GetTypeRegistry().ItemByTypeInfo[
    TypeInfo(CustomerContentSearchRequest_ResponseGroupArray)]
    .RegisterExternalPropertyName(sARRAY_STYLE,sEmbedded);
(...)

The last instruction set the array style to “Embedded” and so the SOAP formatter will serialize the array accordingly.
[edit] Test cases
The toolkit uses FPCUnit for test cases. The test project is located in the \tests\test_suite folder.
The Delphi tests suite is based on Dunit and is located in the \tests\test_suite\delphi folder.
[edit] TODO

[edit] TODO Common
Simple type support in headers
Header support for the binary format
True Attribute serialization support for the binary format
[edit] TODO Client-Side
Basic array support for SOAP
Basic array support for Binary format
XML-RPC formatter
More documentation and samples !
eBay basic client : demonstrates the GetPopularKeywords operation call
"Amazon E-Commerce Service" sample
WSDL to pascal compiler
Enhance the pascal parser : the toolkit now uses the fcl-passrc
Client side services extensions
import functions from XMLRPC server via introspect http://scripts.incutio.com/xmlrpc/introspection.html
[edit] TODO Server-Side
Extend the toolkit to Server side for :
SOAP
Binary serialization
Bytes ordering in binary serialization : alaways use big-endian
XML-RPC
TCP transport ( first implementation).
WSDL generation
More documentation and samples !
Apache support : services as Apache modules using Sekelsenmat 's Apache headers translation
See the apache_module sample located in the tests\apache_module folder
Classes inheritance generation in WSDL
[edit] Licence

The support units are provided under modified LGPL
ws_helper sources are provided under GPL 2 ( or later version )
[edit] Download & NewsGroup

The lastest version can be found at http://inoussa12.googlepages.com ... kitforfpc%26lazarus and from Lazarus-CCR sourceforge site.
The developement sources can be obtained by svn at https://lazarus-ccr.svn.sourcefo ... zarus-ccr/wst/trunk
The discussion list is at http://groups.google.com/group/wst-list
[edit] FAQ

The Web Service Toolkit:FAQ.
[edit] Changes Log

[edit] Version 0.5
Lazarus IDE WSDL importer wizard
WSDL GUI editor ( Type library editor )
Listener design : easy server development
Indy HTTP server listener
Indy TCP server listener
Synapse TCP server listener
XMLRPC support : client and server
Switch from the custom pascal parser to fcl-passrc
Server side : implementation objects pooling
Better Delphi support
SOAP XMLRPC and binary format support
DUnit test suite
WST has been tested on
Windows
Linux
Mac OS X PowerPC
Better WSDL parsing
Services configuration in external file
TStringBufferRemotable ( reading a node's raw buffer )
Bugs fixes
[edit] Version 0.4
WSDL to Pascal translation support in "ws_helper"
new TCP transport implementation ( using synapse library ) in synapse_tcp_protocol.pas
new library protocol implementation in library_protocol.pas
TCP server implementation ( using synapse library ) in synapse_tcp_server.pas
Delphi : first binary format support
Embedded array support
Object Pascal reserved words can now be used as service methods's name, enumeration, ... ( see TTypeRegistryItem.RegisterExternalPropertyName() )
The toolkit can now be used with FPC without Lazarus
"Amazon E-Commerce Service" sample
Bugs Fixes.
[edit] Version 0.3.1 ( 21 August 2006 )
Apache Web Server support : services hosting as Apache's module.
See the apache_module sample located in the tests\apache_module folder.
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.
Bugs Fixes.
[edit] Version 0.3 ( 5 August 2006 )
Header support ( client and server side )
Server side service extensions
Attribute serialization
New transport implementation : Synapse HTTP client support
Address per operation support ( client side )
Extended meta data
Bug Fixes
[edit] Version 0.2.3.2 ( 5 July 2006 )
ICS and Indy HTTP Proxy "user" and "passeword" support.
[edit] Version 0.2.3 ( 4 July 2006 )
WSDL generation
Metadata service
Metadata Browser sample
HTTP server sample
Pascal basic types array implementation
The ws_helper's parser now supports:
{} comment style in the input file
service interfaces may have GUID
more test cases
bug fix
[edit] Version 0.2.2 ( 7 June 2006 )
All pascal basic types are supported by the SOAP serializer and the Binary serializer ( Available integers, available floats, string, boolean, Enumerations, class intances )
Array support for Binary serializer
FPCUnit test cases
SOAP serializer ( basic types and classes instances )
Binary serializer ( basic types and classes instances )
All interfaces now have GUID
[edit] Author

作者地址:, http://inoussa12.googlepages.com/

原文地址:
http://wiki.lazarus.freepascal.org/Web_Service_Toolkit

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册(注册审核可向QQ群索取)

x
回复

使用道具 举报

*滑块验证:

本版积分规则

QQ|手机版|小黑屋|Lazarus中国|Lazarus中文社区 ( 鄂ICP备16006501号-1 )

GMT+8, 2025-5-2 21:06 , Processed in 0.035041 second(s), 10 queries , Redis On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表