Web Service Toolkit for FPC & Lazarus
0.3.1

Inoussa OUEDRAOGO

Table des matières
Web Service Toolkit for FPC & Lazarus	4
Client Side ( service consumption )	5
overview	5
Example	5
Connection Parameters 	8
HTTP Proxy Parameters 	8
Multi-Address service ( Address per operation )	9
Server Side ( service creation )	10
Overview.	10
Example	10
Defining the service Interface	10
Providing an implementation for the service	11
Providing a binder for the service.	13
Host the service into an application server.	14
WSDL generation.	17
The Meta data Service.	18
WSDL generation API.	18
WSDL Customization.	18
Sample	18
Services Extensions – Server side	18
Services meta data.	20
Extended Meta data.	20
Headers support.	22
The  THeaderBlock class 	22
Defining header class.	22
The ICallContext interface.	22
Client side headers.	23
Server side headers.	23
The  ICallControl interface.	23
SOAP Specific.	25
Binding style.	25
Client side.	25
Server side.	25
Encoding style.	25
Client side.	25
Server side.	25
Provided examples	26
Client side examples	26
Server side examples	26
Status	26
Serialization	26
SOAP serializer	26
Binary serializer	27
Class type serialization. 	27
The  root “TBaseRemotable” class.	28
The “TBaseComplexRemotable” serialization.	28
Attribute properties.	29
Provided array implementations.	29
Test cases	30
Author	30

Web Service Toolkit for FPC & Lazarus

“Web Service Toolkit” is a web services package for FPC and Lazarus;  “Web Service Toolkit” is meant to ease web services consumption and creation by FPC and Lazarus users.

Client Side ( service consumption )
overview

“Web Service Toolkit” is made of two parts, a command line tool “ws_helper” and a collection of support units. Given an interface definition file( describing à web service ), “ws_helper” will create a  FPC  unit containing a proxy implementing that interface.  At runtime when a call targeting the web service is issued, the proxy's role is to :

(a)	marshall the call parameters, 
(b)	make the call to the target web service, 
(c)	receive the call return and unmarshall output parameters to the caller.
	
Behind the scene, the proxy will take care of the SOAP plumbing details.

Example
We will use the “google web api”, freely available for personal use at this address “http://www.google.com/apis/”.In order to use this service, we have to translate its exposed WSDL interface to Pascal langage. By now “Web Service Toolkit”  does not contain a WSDL to pascal compiler, so we will assume this translation available as below ( this is an incomplete translation, but it's enough for the sample ).

[code]
Unit googlewebapi;
{$mode objfpc}{$H+}
interface
uses SysUtils, Classes;

Type
  IGoogleSearch = Interface
    function doSpellingSuggestion(
      const key:string;
      const phrase:string
    ):string;
  End;

Implementation
End.
[/code]

Invoking “ws_helper” at the prompt with the file “googlewebapi.pas” as argument will produce a file “googlewebapi_proxy.pas” as below;

[code]
Unit googlewebapi_proxy;
{$mode objfpc}{$H+}
Interface
Uses SysUtils, Classes, base_service_intf, service_intf, googlewebapi;

Type
  TGoogleSearch_Proxy=class(TBaseProxy,IGoogleSearch)
    Protected
      class function GetServiceType() : PTypeInfo;override;
      function doSpellingSuggestion(
        Const key : string; 
        Const phrase : string
      ):string;
  End;

Implementation
uses LResources, metadata_repository;

{ TGoogleSearch_Proxy implementation }

class function TGoogleSearch_Proxy.GetServiceType() : PTypeInfo;
begin
  result := TypeInfo(IGoogleSearch);
end;

function TGoogleSearch_Proxy.doSpellingSuggestion(
  Const key : string; 
  Const phrase : string
):string;
Var
  locSerializer : IFormatterClient;
  strPrmName : string;
Begin
  locSerializer := GetSerializer();
  Try
    locSerializer.BeginCall('doSpellingSuggestion', GetTarget(),(Self as ICallContext));
      locSerializer.Put('key', TypeInfo(string), key);
      locSerializer.Put('phrase', TypeInfo(string), phrase);
    locSerializer.EndCall();

    MakeCall();

    locSerializer.BeginCallRead((Self as ICallContext));
      strPrmName := 'return';
      locSerializer.Get(TypeInfo(string), strPrmName, result);

  Finally
    locSerializer.Clear();
  End;
End;
initialization
  {$i googlewebapi.lrs}

  {$IF DECLARED(Register_googlewebapi_ServiceMetadata)}
  Register_googlewebapi_ServiceMetadata();
  {$ENDIF} 
End.
[/code]

Then we can build a sample program for the “IGoogleSearch” service(see below). In order to compile this program you must have Indy ( tested with Indy10 ) which can be found at this location “http://www.indyproject.org/Sockets/index.en.iwp” or ICS(  tested with the latest ICS-V5 Distribution ) at that address “http://www.overbyte.be/frame_index.html”  as it is used for the HTTP protocol.

[code]
program test_google_api;
{$mode objfpc}{$H+}
uses
  Classes, SysUtils,
  service_intf, soap_formatter,
  //indy_http_protocol,
  ics_http_protocol,
  googlewebapi, googlewebapi_proxy;

Const
  sADDRESS = 'http:address=http://api.google.com/search/beta2';
  sTARGET = 'urn:GoogleSearch';
  sKEY    = '<your google key here>';
  sSERVICE_PROTOCOL = 'SOAP';
Var
  tmpObj : IGoogleSearch;
  strBuffer : string;
begin
  ICS_RegisterHTTP_Transport();
  WriteLn();
  WriteLn('Enter phrase to spell :');
  ReadLn(strBuffer);
  tmpObj := TGoogleSearch_Proxy.Create(sTARGET,sSERVICE_PROTOCOL,sADDRESS);
  Try
    strBuffer := tmpObj.doSpellingSuggestion(sKEY,strBuffer);
    WriteLn('google spell >>> ',strBuffer);
  Except
    On E : Exception Do
      WriteLn(E.Message);
  End;

  ReadLn();
end.
[/code]

The units  service_intf, soap_formatter, indy_http_protocol, ics_http_protocol as provided with this toolkit; Below is the result of a execution session spelling for “freepscal lzarus” written with a missing letter 'a' beetwen the letter 'p' and the the letter 's' and a missing letter 'a' between the letter 'L' and the the letter 'z' .

[Listing]
E:\Inoussa\Sources\lazarus\wst\v0.2>  .\tests\google_api\test_google_api

Enter phrase to spell :
freepscal lzarus
google spell >>> freepascal lazarus
[/Listing]

Google spells it correctly : “freepascal lazarus”!

Connection Parameters 
HTTP Proxy Parameters 
HTTP Connection through proxy are supported. Below is an example address string. 
  Const
    sADDRESS = 'http:address=http://api.google.com/search/beta2'+
                      ';ProxyServer=197.150.10.10;ProxyPort=9881'+
                      ';ProxyUsername=inoussa;ProxyPassword=wst';

The general format is: 

   protocol:paramName=paramValue(;paramName=paramValue)*

For HTTP the supported parameters are: 
•	address ( required for a service supporting a unique address ) 
•	ProxyServer 
•	ProxyPort 
•	ProxyUsername 
•	ProxyPassword 
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.

Server Side ( service creation )
Overview.
Web Service Toolkit contains a server side framework 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 and a binary protocol,
•	The framework is not bound to a transport protocol.

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

Defining the service Interface
We will use the interface defined above for our sample. The complete projects of the example is located in the folder “\tests\tcp_server\calculator”. 

[Code]
unit calculator;
{$mode objfpc}{$H+}
interface
uses SysUtils, base_service_intf;

Type

  TBinaryArgsResult = class(TBaseComplexRemotable)
  private
    FArg_A: Integer;
    FArg_B: Integer;
    FArg_OP: string;
    FArg_R: Integer;
  Published
    Property Arg_A : Integer Read FArg_A Write FArg_A;
    Property Arg_B : Integer Read FArg_B Write FArg_B;
    Property Arg_R : Integer Read FArg_R Write FArg_R;
    Property Arg_OP : string Read FArg_OP Write FArg_OP;
  End;

  ICalculator = Interface
    function AddInt(
      Const A:Integer;
      Const B:Integer
    ):TBinaryArgsResult;
    function DivInt(
      Const A:Integer;
      Const B:Integer
    ):Integer;
  End;

implementation
uses base_soap_formatter;

Initialization
  GetTypeRegistry().Register(sXSD_NS,TypeInfo(TBinaryArgsResult),'TBinaryArgsResult');
  
end.
[/code]

Providing an implementation for the service
“ws_helper” has options to generate proxy file, basic implementation skeleton file and a binder file ( see the listing above). 

[Listing ws_helper usage]
ws_helper [-p] [-b] [-i] [-oPATH] inputFilename
  -p  Generate service proxy
  -b  Generate service binder
  -i  Generate service minimal implementation
  -o  PATH  Output directory
[/Listing]

Executing “ws_helper” with the -i and -b options as above will produce two files : calculator_imp.pas and calculator_binder.pas.

[Listing ws_helper execution session]
ws_helper\ws_helper.exe -i -b -osrv tests\tcp_server\calculator\calculator.pas
ws_helper Copyright (c) 2006 by Inoussa OUEDRAOGO
File "tests\tcp_server\calculator\calculator.pas" parsed succesfully.
[/Listing]

The calculator_imp.pas unit contains a skeleton implementation class for the interface. It defines a procedure named RegisterCalculatorImplementationFactory. The procedure registers the  class as the service implementation provider in the implementation registry.

[code]
Unit calculator_imp;
{$mode objfpc}{$H+}
Interface
Uses SysUtils, Classes, 
     base_service_intf, server_service_intf, server_service_imputils, calculator;
Type
  TCalculator_ServiceImp=class(TBaseServiceImplementation,ICalculator)
  Protected
    function AddInt(Const A : Integer;Const B : Integer):TBinaryArgsResult;
    function DivInt(Const A : Integer;Const B : Integer):Integer;
  End;

  procedure RegisterCalculatorImplementationFactory();

Implementation

{ TCalculator_ServiceImp implementation }
function TCalculator_ServiceImp.AddInt( Const A : Integer; Const B : Integer):TBinaryArgsResult;
Begin
  Result := TBinaryArgsResult.Create();
  Try
    Result.Arg_OP := '+';
    Result.Arg_A := A;
    Result.Arg_B := B;
    Result.Arg_R := A + B;
  Except
    FreeAndNil(Result);
    Raise;
  End;
End;

function TCalculator_ServiceImp.DivInt( Const A : Integer; Const B : Integer):Integer;
Begin
  Result := A div B;
End;

procedure RegisterCalculatorImplementationFactory();
Begin
  GetServiceImplementationRegistry().Register('Calculator',TImplementationFactory.Create(TCalculator_ServiceImp) as IServiceImplementationFactory);
End;

End.       
[/code]

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 calculator_binder.pas unit generated by ws_helper, contains :
•	 TCalculator_ServiceBinder : the actual binder class,
•	TCalculator_ServiceBinderFactory a factory class for the binder and
•	Server_service_RegisterCalculatorService : the binder factory registration procedure.
•	
The following code extract shows the unit interface part and a method handler of the binder.

[Code part of calculator_binder.pas]
Unit calculator_binder;
{$mode objfpc}{$H+}
Interface
Uses SysUtils, Classes, base_service_intf, server_service_intf, calculator;

Type
  TCalculator_ServiceBinder=class(TBaseServiceBinder)
  Protected
    procedure AddIntHandler(AFormatter:IFormatterResponse);
    procedure DivIntHandler(AFormatter:IFormatterResponse);
  Public
    constructor Create();
  End;

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

  procedure Server_service_RegisterCalculatorService();

Implementation
uses TypInfo;

procedure TCalculator_ServiceBinder.AddIntHandler(AFormatter:IFormatterResponse);
Var
  tmpObj : ICalculator;
  callCtx : ICallContext;
  strPrmName : string;
  procName,trgName : string;
  A : Integer;
  B : Integer;
  returnVal : TBinaryArgsResult;
  locTypeInfo : PTypeInfo;
Begin
  callCtx := CreateCallContext();
  locTypeInfo := TypeInfo(TBinaryArgsResult);
  If ( locTypeInfo^.Kind in [tkClass,tkInterface] ) Then
    Pointer(returnVal) := Nil;
  
  strPrmName := 'A';  AFormatter.Get(TypeInfo(Integer),strPrmName,A);
  strPrmName := 'B';  AFormatter.Get(TypeInfo(Integer),strPrmName,B);
  
  tmpObj := Self.GetFactory().CreateInstance() as ICalculator;
  
  returnVal := tmpObj.AddInt(A,B);
  locTypeInfo := TypeInfo(TBinaryArgsResult);
  If ( locTypeInfo^.Kind = tkClass ) And Assigned(Pointer(returnVal)) Then
    callCtx.AddObject(TObject(returnVal));
  
  procName := AFormatter.GetCallProcedureName();
  trgName := AFormatter.GetCallTarget();
  AFormatter.Clear();
  AFormatter.BeginCallResponse(procName,trgName);
    AFormatter.Put('return',TypeInfo(TBinaryArgsResult),returnVal);
  AFormatter.EndCallResponse();
  
  callCtx := Nil;
End;
[/Code]

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.

The toolkit is provided with a simple TCP server ( using ICS components ) hosting the sample Calculator  service defined early in this document located in the \tests\tcp_server folder. The complete source files of the sample is in that directory and the client application in the \client  sub folder. The registrations are done in the application's main form OnCreate event as printed above:

[Code]
procedure TfMain.FormCreate(Sender: TObject);
begin
  Server_service_RegisterCalculatorService();
  RegisterCalculatorImplementationFactory();
  Server_service_RegisterSoapFormat();
  Server_service_RegisterBinaryFormat();
end; 
[/Code]

Server_service_RegisterCalculatorService located in the calculator_binder unit ( generated by ws_helper ) registers the Calculator service by calling in turn GetServerServiceRegistry:

[Code located in calculator_binder.pas]
procedure Server_service_RegisterCalculatorService();
Begin
  GetServerServiceRegistry().Register(
    'Calculator',
    TCalculator_ServiceBinderFactory.Create() as IItemFactory
  );
End;
[Code]

RegisterCalculatorImplementationFactory  located in the calculator_imp unit ( generated by ws_helper ) registers the Calculator implementation by calling in turn GetServiceImplementationRegistry:

[Code located in calculator_imp.pas]
procedure RegisterCalculatorImplementationFactory();
Begin
  GetServiceImplementationRegistry().Register(
    'Calculator',
    TSimpleItemFactory.Create(TCalculator_ServiceImp) as IitemFactory
  );
End;
[Code]

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

[Code located in server_service_soap.pas]
procedure Server_service_RegisterSoapFormat();
begin
  GetFormatterRegistry().Register(
    sSOAP_CONTENT_TYPE,
    TSimpleItemFactory.Create(TSOAPFormatter) as IitemFactory
  );
  RegisterStdTypes();
end;
[Code]

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

[Code located in server_binary_formatter.pas]
procedure Server_service_RegisterBinaryFormat();
begin
  GetFormatterRegistry().Register(
    sCONTENT_TYPE,
    TBinaryFormatterFactory.Create() as IitemFactory
  );
end;
[Code]

HandleServiceRequest is invoked to process incoming request buffer in the socket client processing procedure ProcessData located in the server_unit unit. 

[Code]
procedure TTcpSrvApp.ProcessData(Client : TTcpSrvClient);
Var
  buff, trgt,ctntyp : string;
  rqst : IRequestBuffer;
  wrtr : IDataStore;
  rdr : IDataStoreReader;
  inStream, outStream, bufStream : TMemoryStream;
  i : Integer;
begin
  inStream := Nil;
  outStream := Nil;
  bufStream := Nil;
  Try
    Client.RequestStream.Position := 0;
    Try
      inStream := TMemoryStream.Create();
      outStream := TMemoryStream.Create();
      bufStream := TMemoryStream.Create();
      rdr := CreateBinaryReader(Client.RequestStream);
      trgt := rdr.ReadStr();
      ctntyp := rdr.ReadStr();
      buff := rdr.ReadStr();
      inStream.Write(buff[1],Length(buff));
      inStream.Position := 0;
      rqst := TRequestBuffer.Create(trgt,ctntyp,inStream,bufStream);
      HandleServiceRequest(rqst);
      i := bufStream.Size;
      SetLength(buff,i);
      bufStream.Position := 0;
      bufStream.Read(buff[1],i);
      wrtr := CreateBinaryWriter(outStream);
      wrtr.WriteStr(buff);
      Client.Send(outStream.Memory,outStream.Size);
    Finally
      bufStream.Free();
      outStream.Free();
      inStream.Free();
      Client.FDataLentgh := -1;
      Client.RequestStream.Size := 0;
    End;
  Except
    On e : Exception Do
      Display('ProcessData()>> Exception = '+e.Message);
  End;
end;
[/Code]
 
In order to give it a try one have to :
1.	compile the server,
2.	compile the client application,
3.	execute the server and start listening,
4.	execute the client.

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.
The Meta data 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.
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);

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.
Sample
A functional sample project is located under \tests\http_server . It is an Indy base http server.

Services Extensions – Server side
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 calculator  sample.

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 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; 

Extended Meta data.
The meta data interface provides a way to add custom data to recorded ones. Currently only operation's meta data are supported through the SetOperationCustomData method.
A repository's extended meta data has to be registered after the service meta data recorded in the Lazarus 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.  

Headers support.
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.
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; 

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.
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 := 121076;
    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.
Server side headers.
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 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; 

SOAP Specific.
Binding style.
The binding style is used to indicate whether the service is “RPC” oriented or “Document” oriented.
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'
                           ); 
Server side.
Currently services created with the toolkit use the “RPC” binding style.
Encoding style.
The encoding style indicates the rules used to encode types in XML. Supported values are “Encoded”  and “Litteral”.
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.
Server side.
Currently services created with the toolkit use the “Encoded” encoding style.

Provided examples
The HTTP server side samples has been tested on Windows XP and Ubuntu. The client side has been tested under  Windows XP and Ubuntu. The samples are located under the “tests” folder.
Client side examples
•	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/ ).

Server side examples
•	TCP server : This is a sample TCP server based on the ICS components. It uses the calculator service.
•	HTTP server : This is a sample HTTP server based on the Indy10 components. It uses the calculator service and the toolkit metadata service. It demonstrates the WSDL generation.
•	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 calculator service and the toolkit metadata service. It demonstrates the WSDL generation.

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. 
The toolkit is provide with a SOAP 1.1 message format implementation  ( based on the FCL XML units) and a binary one.
Serialization
The serialization is based on the IFormatterBase interface located in the base_service_intf unit.

The toolkit has two serializers implementations : the SOAP serializer and a binary serializer.

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

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

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.

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.
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.
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
 
Test cases
•	The toolkit uses FPCUnit for  test cases. The test project is located in the \tests\test_suite folder. 

Author
Inoussa OUEDRAOGO, inoussa12@gmail.com.