Web Service Toolkit/ru

From Free Pascal wiki
Jump to: navigation, search

English (en) français (fr) português (pt) русский (ru) 中文(中国大陆)‎ (zh_CN)

«Web Service Toolkit» - это пакет веб-сервисов для FPC, Lazarus и Delphi, «Web Service Toolkit» предназначен для облегчения использования и обслуживания веб-сервисов пользователями FPC, Lazarus и Delphi.

Contents

Сторона клиента (использование сервиса)

Mailing list: http://groups.google.com/group/wst-list

Subversion checkout: svn co https://svn.code.sf.net/p/lazarus-ccr/svn/wst/trunk

Обзор

“Web Service Toolkit” состоит из двух частей :

  • набор программ: “typ_lib_edtr” редактор библиотеки типов WSDL, утилита командной строки “ws_helper” и пакет интеграции Lazarus, содержащий несколько "мастеров",
  • набор модулей времени выполнения.

На основе файла описания интерфейса (файл WSDL или файл pascal, описывающий веб-сервис) «ws_helper» (или мастера импорта файлов WSDL в самом Lazarus) создаст модуль объектного Паскаля, содержащего прокси, который и реализует этот интерфейс. Во время выполнения, когда происходит вызов работы веб сервиса, роль прокси модуля заключается в следующем:

  • передача параметров вызова (запроса),
  • выполнение вызова требуемого веб-сервиса,
  • получение ответа запроса и демаршализация выходных параметров вызывающему.

Таким образом, прокси модуль позаботится о деталях [или скроет их] реализации SOAP.

Пример

Мы будем использовать пример «сервиса пользователя» («user service»), который поставляется с WST. Схема, описывающая службу, находится в папке \samples, файл - user_service_intf.wsdl. Чтобы использовать эту службу, мы должны перевести открытый интерфейс службы, который представен Языком описания веб-сервисов (т.е. WSDL) на язык Object Pascal. Нам потребуется скомпилировать и запустить сервер, предоставляющий этот сервис. Чтобы сделать это, пожалуйста, скомпилируйте проект, который находится в \samples\http_server, затем запустите полученную программу, чтобы в реальности запустить данный сервис; Обратите внимание, что в последних выпусках ОС Microsoft Windows Вам также может потребоваться включить службу для обеспечения правильной работы в сети. Чтобы перевести определения службы на язык Object Pascal в Lazarus мы можем использовать "мастер" импорта; мы можем также использовать 'ws_helper' как отдельную программу.

Мастер “импорта WSDL”

Этот мастер содержится в пакете wst_design.lpk, расположенном в каталоге \ide\lazarus данного инструментария (WST). После установки пакета в меню «Проект» в Lazarus добавляется раздел «Web Services Toolkit» («Инструментарий веб служб») с двумя подменю:

  • Import WSDL file... (импорт файла WSDL)
  • Type Library Editor... (Редактор библиотеки типов)

меню мастера Web Services Toolkit в Lazarus.


При клике на первом пункте меню (Import WSDL file...) вызывается диалоговое окно. Мы указываем WSDL файл и папку, в которой будут сохраняться сгенерированные файлы и нажимаем кнопку OK для завершения.

Мастер импорта WSDL в Web Services Toolkit Lazarus.


Мастер генерирует два файла:

  • user_service_intf.pas, файл описания сервиса(Pascal-эквивалент WSDL)
  • user_service_intf_proxy.pas, этот файл содержит прокси, который реализует службу интерфейса, описанного в первом файле.

Импорт при помощи ws_helper.

Программа ws_helper это версия для командной строки программы-мастера импорта. Для того, чтобы представить его возможности, ниже дан вывод утилиты с описанием ключей командной строки.

  ws_helper [-uMODE] [-gOPTION] [-p] [-b] [-i] [-w] [-x] [-y] [-d] -[fSPECIFACTIONS] [-oPATH] [-aPATH] 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
    -g  Code generation option, with the following options : 
      A  : object arrays are generated as "array" derived from TBaseObjectArrayRemotable
      C  : object arrays are generated as "collection" derived from TObjectCollectionRemotable
      EP : enum type's items are prefixed with the enum name
      EN : enum type's items are not prefixed with the enum name, the default
    -p  Generate service proxy
    -b  Generate service binder
    -i  Generate service minimal implementation. This will erase any existing implementation file!
    -o  PATH  Relative output directory
    -a  PATH  Absolute output directory
    -w  Generate WSDL file; Can be used to get wsdl from pascal
    -x  Generate XSD file; Can be used to get xsd from pascal
    -y  Generate easy access interface for wrapped parameters
    -d  Generate documentation as comment in the interface file
    -c  Indicate the parser's case sensitivity : 
      S  : the paser is case sensitive
      I  : the paser is not case sensitive
    -f  Specify unit(s) renaming option : oldName= NewName(;oldName= NewName)* 

Для перевода user service файла WDSL примера запустите нижеследующую команду:

  ws_helper.exe -uA -p -o. user_service_intf.wsdl
  ws_helper, Web Service Toolkit 0.6 Copyright (c) 2006-2014 by Inoussa OUEDRAOGO

  Parsing the file : user_service_intf.wsdl
  (...)
  Interface file generation...
  Proxy file generation...
  Metadata file generation...
  File "user_service_intf.wsdl" parsed succesfully.


ниже показан вывод обоих файлов:

unit user_service_intf;
(...)
interface
 
uses SysUtils, Classes, TypInfo, base_service_intf, service_intf;
 
const
  sNAME_SPACE = 'urn:UserService';
  sUNIT_NAME = 'user_service_intf';
 
type
 
  TUserArray = class;
  TUser = class;
  TNote = class;  
(...)
  UserService = interface(IInvokable)
    ['{7537A085-DCD1-4B24-8B74-BC35ACB4896D}']
    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 Register_user_service_intf_ServiceMetadata();
Unit user_service_intf_proxy;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
Interface
 
Uses SysUtils, Classes, TypInfo, base_service_intf, service_intf, user_service_intf;
 
Type
 
  TUserService_Proxy=class(TBaseProxy,UserService)
  Protected
    class function GetServiceType() : PTypeInfo;override;
    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;
 
  Function wst_CreateInstance_UserService(const AFormat : string = 'SOAP:'; const ATransport : string = 'HTTP:'; const AAddress : string = ''):UserService;
 
Implementation
uses wst_resources_imp, metadata_repository;
 
 
Function wst_CreateInstance_UserService(const AFormat : string; const ATransport : string; const AAddress : string):UserService;
Var
  locAdr : string;
Begin
  locAdr := AAddress;
  if ( locAdr = '' ) then
    locAdr := GetServiceDefaultAddress(TypeInfo(UserService));
  Result := TUserService_Proxy.Create('UserService',AFormat+GetServiceDefaultFormatProperties(TypeInfo(UserService)),ATransport + 'address=' + locAdr);
End;
 
function TUserService_Proxy.GetList():TUserArray;
Var
  locSerializer : IFormatterClient;
  locCallContext : ICallContext;
  locStrPrmName : string;
Begin
  locCallContext := Self as ICallContext;
  locSerializer := GetSerializer();
  Try
    locSerializer.BeginCall('GetList', GetTarget(),locCallContext);
    locSerializer.EndCall();
 
    MakeCall();
 
    locSerializer.BeginCallRead(locCallContext);
      TObject(Result) := Nil;
      locStrPrmName := 'result';
      locSerializer.Get(TypeInfo(TUserArray), locStrPrmName, Result);
 
  Finally
    locSerializer.Clear();
  End;
End;
(...)

Теперь мы готовы собрать простую программу для этого сервиса. Это простая клиентская программа вызывает метод GetList данного сервиса, чтобы получить список пользователей, которые уже зарегистрированы в этой службе; Список в этом случае выводится на экран:

program user_client_console;
uses SysUtils, fpc_http_protocol, soap_formatter,
     user_service_intf_proxy, user_service_intf;
 
var
  locService : UserService;
  items : TUserArray;
  item : TUser;
  i : Integer;
begin
  FPC_RegisterHTTP_Transport();
  locService := wst_CreateInstance_UserService();
  items := locService.GetList();
  try
    WriteLn('WST User Service Sample',sLineBreak);
    if (items.Length = 0) then
      WriteLn('  No user found.')
    else
      WriteLn(Format('  %d user(s) found : ',[items.Length]));
    for i := 0 to items.Length - 1 do begin
      item := items[i];
      WriteLn(Format('    Name= "%s", e-Mail= "%s"',[item.UserName,item.eMail]));
    end;
  finally
    items.Free();
  end;
end.

Модули base_service_intf, service_intf, soap_formatter, fpc_http_protocol, wst_resources_imp and metadata_repository предоставлены wst; Ниже результат выполнения:

WST User Service Sample

  2 user(s) found :
    Name= "Lazarus FreePascal", e-Mail= "Lazarus@FreePascal.wst"
    Name= "Inoussa OUEDRAOGO", e-Mail= "sample@example.wst"

Найдено два пользователя. Функция wst_CreateInstance_UserService(), расположенная в файле user_service_intf.pas, создает экземпляр прокси, основанная на информации службы, содержащейся в файле WSDL. Полностью исходный код примера поставляется вместе с инструментарием WST библиотеки в папке \samples\user_client_console; Обратите внимание, что пример, представленной в этой папке более полный, чем приведенный на этой странице

Параметры соединения

Основной формат:

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

Параметры HTTP прокси

Для HTTP поддерживаемые параметры приведены ниже:

  • Address ( требуется для адресов, поддердивающих уникальные адреса )
  • ProxyServer
  • ProxyPort
  • ProxyUsername
  • ProxyPassword

HTTP соединение через прокси поддерживается. Ниже пример адресной строки.

  const
    sADDRESS = 'http:address=http://webservices.amazon.com/AWSECommerceService/2007-04-04'+
                 ';ProxyServer=197.150.10.10;ProxyPort=9881'+
                 ';ProxyUsername=inoussa;ProxyPassword=wst';

Инструментарий имеет 3 HTTP реализации, основанная на встроеной реализации FPC (fpc_http_server.pas), Synapse ( synapse_http_protocol.pas ) и Indy (indy_http_protocol.pas).

Параметры TCP соединения

Поддерживаемые TCP параметры:

  • address ( требуется для служб, поддерживающие уникальные адреса)
  • Port
  • target( целевая служба )

Ниже приведена примерная адресная строка

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

Инструментарий имеет 3 TCP реализации, основанные на встроенной реализации в FPC (fpc_tcp_server.pas), Synapse (synapse_tcp_protocol.pas) и Indy (indy_tcp_protocol.pas).

Параметры соединения через библиотеки (LIB)

Идея этого протокола в том, чтобы иметь возможность подключаться к службам через динамические библиотеки (DLL/DSO). Это может рассматриваться как плагин фреймворк, где плагины (службы) обеспечены динамическими библиотеками.

Параметры соединения LIB:

  • FileName (имя файла DLL/SO)
  • target (целевая служба)
Ниже пример адресной строки
  Const
    sADDRESS = 'LIB:FileName=..\library_server\lib_server.dll;target=UserService';

Инструментарий имеет одну реализацию LIB соединения (library_protocol.pas).

Папка с примерами содержит 4 проекта user_client_console, tcp_server, http_server и library_server, которые демонстрируют протоколы TCP, HTTP и протокол типа "библиотека"

Параметры соединения с тем же процессом (SAME_PROCESS)

Идея этого протокола, дать возможность подключаться к службе в самом клиентском процессе. Это позволит собрать многоуровневое приложение в рамках уникальной программы, которая может быть преобразована в полностью многоуровневое приложение. Это также может быть использовано для облегчения отладки приложения

SAME_PROCESS поддерживает следующие параметры:

  • Address (целевая служба)

Ниже пример адресной строки

  Const
    sADDRESS = 'SAME_PROCESS:Address=UserService';

Инструментарий имеют одну реализацию (same_process_protocol.pas).

Мульти-адресный сервис (Адрес на операцию)

Некоторые сервисы (как пример, eBay SOAP сервисы) используют отдельный адрес на каждую операцию. Инструментарий использует расширенные мета данные (см. главу о мета данных сервисов) для установки адресов операций. “Ebay” SOAP пример, расположенный в tests\ebay папке демонстрирует настройку адреса операции.

Решение проблем

'Сообщение об ошибке: Invalid parameter : "AProtocolData"'

Это случается, когда неизвестно какой протокол следует использовать. Убедитесь, что правильный форматтер выбран (к примеру: 'SOAP:') и что указанные форматтер зарегистрирован. Это делается добавлением соответствующего модуля xxx_formatter в Ваш блок uses. (для примера: soap_formatter)

Сторона сервера (создание сервиса)

Обзор

Инструментарий веб-сервиса содержит фреймворк для серверной стороны и редактор библиотеки типов на базе языка WSDL для создания сервиса. Ключевые особенности:

  • Описание сервиса (интерфейса) отделено от реализации,
  • Интерфейс и реализация не связаны с протоколом сообщений,
  • генератор WSDL
  • Поддержка для сериализации SOAP 1.1
  • Поддержка для сериализации xmlrpc
  • Поддержка пользовательской бинарной сериализации
  • Платформа не привязана к транспортному протоколу
  • Легко добавить поддержку для серверов приложений.

Пример

Для того, чтобы создать сервис,Вы должны:

  • описать его интерфейс,
  • обеспечить реализацию и зарегистрировать его для сервиса,
  • обеспечить связку, которая будет перенаправлять запросы на целевой сервис на реализацию и ее регистрацию,
  • разместить сервис на сервере приложения (TCP сервер, HTTP сервер, LIBRARY сервер, ...).

Описание Интерфейса (Interface) сервиса

Начиная с версии 0.5, WST обеспечивает редактор библиотеки типов WSDL для описания типов и сервисов, используемых при реализации. Рисунок (3) ниже представляет основной интерфейс этого инструмента. Редактор библиотеки типов представлен как

  • "мастер" Lazarus в виде пункта меню “Проект/Web Services Toolkit/Type Library Editor...”
  • отдельная программа typ_lib_edtr.exe.

Редактор библиотеки типов Web Services Toolkit


Особенности Редактора Библиотеки Типов:

  • Графический интерфейс пользователя
  • Просмотр исходных текстов WSDL
  • Просмотр Pascal код библиотеки
  • Просмотр Pascal кода прокси
  • Просмотр "скелета" кода Pascal
  • Просмотр Pascal прокси биндера (связки)
  • создание интерфейса перечисления
  • интерфейс создания классов
  • интерфейс создания массивов
  • интерфейс создания алиасов типов
  • интерфейс создания интерфейсов сервисов.

Ниже приведен пример на изображении. Мы будем использовать файл user_service_intf.wsdl, расположенный в \samples directory для нашего примера.

Редактор Библиотеки типов Web Services Toolkit, редактор классов.

Редактор Библиотеки типов Web Services Toolkit, редактор интерфейса сервиса

Экспорт файлов Pascal.

Редактор Библиотеки Типов имеет возможность генерировать Pascal версию файла WSDL. Чтобы сделать это, следует кликнуть “Files\Save generated files ...” (это также может быть сделано через контекстное меню); Это вызовет диалоговое окошко как показано в изображении ниже. Редактор Библиотеки Типов, экспорт в файлы Pascal.

Нажмите OK для завершения. Программа ws_helper имеет те же возможности и файлы могут сгенерированы с помощью нижеследующей команды:

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.

Полностью проект расположен в папке “samples”. Ниже приведен вывод сгенерированных файлов интерфейса.

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;
(...)

Обеспечение реализации сервиса

Модуль user_service_intf_imp.pas, сгенерированный выше содержит скелет реализации класса для интерфейса. Это определяет процедуру, названную RegisterUserServiceImplementationFactory. Процедура регистрирует класс как провайдер реализации сервиса в реестре реализаций.

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)
  Protected
    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” имеет возможность генерировать прокси файл, файл скелета реализации и биндер файл (файл связки) (см. листинг ниже).

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  PATH  Relative output directory
  -a  PATH  Absolute output directory

Начало файла реализации может также создать, используя ws_helper с ключами -i и -b как упомянуто выше;

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

Обеспечение биндера для веб-сервиса

Роль биндера (связки) в следующем:

  • распаковка входящих сообщений,
  • установка стека вызовов,
  • сделать вызов от зарегистрированной реализации,
  • сериализовать стека выполнения для создания ответного сообщения.

Модуль user_service_intf_binder.pas, сгенерированный выше, содержит:

  • TUserService_ServiceBinder: действительный класс биндера,
  • TUserService_ServiceBinderFactory: фабрика классов для биндера и
  • Server_service_RegisterUserServiceService: процедура регистрации фабрики биндера.

Нижеследующий код вывода показывает интерфейсную часть модуля и обработчик метода биндера.

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)
  Protected
    procedure GetListHandler(AFormatter:IFormatterResponse);
    procedure AddHandler(AFormatter:IFormatterResponse);
    procedure UpdateHandler(AFormatter:IFormatterResponse);
    procedure FindHandler(AFormatter:IFormatterResponse);
    procedure DeleteHandler(AFormatter:IFormatterResponse);
  Public
    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;

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 two (2) HTTP listeners implementations:

  • TwstFPHttpListener (fpc_http_server.pas), using the FPC's native network component.
  • TwstIndyHttpListener ( indy_http_server.pas ),

Three (3) TCP listeners implementations are provided:

  • TwstFPCTcpListener (fpc_tcp_server.pas), using the FPC's native network component.
  • 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 an 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  := TwstFPHttpListener.Create();
  AppObject.Start();


Complete listing :

program http_server;
{$mode objfpc}{$H+}
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, SysUtils,
  fpc_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 := TwstFPHttpListener.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 :

  1. compile the server ( \samples\tcp_server\tcp_server.lpi it is a console program),
  2. compile the client application ( \samples\user_client_console\user_client_console.lpi ),
  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 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.

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 \samples\http_server . It is an Indy base http server.

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

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

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;

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 shown 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 shown 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;overload;
    function AddHeader(
      const AHeader        : TBaseRemotable;
      const AKeepOwnership : Boolean;
      const AName          : string = ''
    ):Integer;overload;
    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 shown 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.

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 with the -i command line option. The method printed below, 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;


Headers that does not derived from THeaderBlock.

Classes that inherit from TBaseRemotable can also be used as headers; In that case a THeaderBlockProxy’s instance is automatically created and used as a wrapper to allow a TBaseRemotable instance to be sent and received as a header block. The ICallContext has an overloaded AddHeader method that accepts TBaseRemotable’s instances.

Специфика SOAP

Стиль связывания

Стиль связывания используется для определения того, сервис - RPC-ориентированный или Document-ориентированный.

Клиентская часть

Стиль связывания может быть прописан в строке SOAP протокола при создании прокси сервиса. Значение по умолчанию для стиля связывания RPC. Ниже показан пример, демонстрирующий использования Document-стиля.

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

Серверная сторона

На настоящий момент сервисы, созданные в инструментарии используют стиль связывания RPC

Стиль кодирования

Стиль кодирования показывает правила, используемые для кодирования типов в XML. Поддерживаемые значения Encoded и Literal.

Клиентская часть

Стиль кодирования может быть указан с помощью строки протокола SOAP при создании прокси сервиса. Значение по умолчанию для стиля кодирования - Encoded. Вышеприведенный пример демонстрирует использования стиля Literal.

Серверная часть

На настоящий момент сервисы, который создаются с помощью инструментария используют стиль кодирования Encoded.

Приведенные примеры

Примеры, расположенные в папке “tests”.

Примеры на стороне клиента ( протестировано на Windows XP, Ubuntu и MacOS на PowerPC)

  • UserService, samples\http_server, samples\tcp_server, samples\user_client_console, sample\library_server: клиентская консоль использует три клиент-серверных протокола (HTTP, TCP, LIBRARY)
  • Google пример: Демонстрирует использование классов и типов массива данных.
  • Metadata Браузер: Этот пример демонстрирует использование класса и типа массивов данных и, главным образом, инструментария сервиса метаданных.
  • eBay пример, этот пример использует OpenSLL, который может быть найден на http://www.openssl.org и SYNAPSE (http://www.ararat.cz/synapse/).
  • \samples\delphi\ : эта папка содержит Delphi (компилируется с Delphi 7) примеры клиента и сервера. Используемый протокол: TCP, HTTP, LIBRARY; Используемые форматы: SOAP, XMLRPC и BINARY.

Примеры со стороны сервера

  • samples\tcp_server : это пример TCP сервера, основанного на компонентах Synapse. Использует UserService.
  • samples\http_server : Это пример HTTP сервера, основанного на компонентах Indy10. Использует UserService инструментарий сервиса метаданных. Демонстрирует генерация WSDL.
  • samples\apache_module : Пример модуля Apache, этот пример демонстрирует размещение инструментария на веб-сервере Apache. Основан на переводе (Sekelsenmat) заголовков Apache. Использует сервис UserService и инструментарий сервиса метаданных. Демонстрирует генерацию WSDL.

Статус

Инструментарий может использоваться как для простых типов, так и для типов классов. Сериализация предназначена для настройки базовых типов и типов классов путем реализации классов, производных от “TBaseRemotable”. Эти классы должны быть зарегистрированы в реестре типа.

Сериализация

Сериализация основана на интерфейсе IFormatterBase, расположенном в модуле base_service_sintf.

Инструментарий содержит четыре реализации сериализаторов: сериализатор SOAP, сериализатор XMLRPC, сериализатор JSONRPC и бинарный сериализатор. Этот сериализатор был протестирован на FPC 2.x и Delphi 7.

сериализатор SOAP

Сериализатор SOAP реализует SOAP 1.1. Он поддерживает следующие типы Pascal:

  • Доступные целочисленные типы:
    • Byte приводится к unsignedByte
    • ShortInt приводится к byte
    • SmallInt приводится к short
    • Word приводится к unsignedShort
    • LongInt приводится к int
    • LongWord приводится к unsignedInt
    • Int64 приводится к long
    • QWord приводится к int
  • String приводится к string
  • Boolean приводится к boolean
  • Enumerations приводится к их строковому представлению
  • Float types :
    • Single приводится к float
    • Double приводится к double
    • Extended приводится к double
    • Currency приводится к float
  • Object (экземпляр класса): Инструментарий имеет поддержку экземпляров классов, наследуемых от TBaseRemotable. TBaseRemotable это базовый класс, использованный в форматтере интерфейса для возможности настройки сериализации. Инструментарий предоставляет класс TBaseComplexRemotable, который реализует сериализацию для него (или его потомков) опубликованных [published] свойств.

Бинарный сериализатор

Бинарный сериализатор более оптимизирован по скорости и размеру в сравнении с SOAP сериализацией. Используется big-endian для потоковой передачи данных. Имеет поддержку нижеприведенных паскалевских типов:

  • Доступные целочисленные:
    • Byte
    • ShortInt
    • SmallInt
    • Word
    • LongInt
    • LongWord
    • Int64
    • QWord
  • String
  • Boolean
  • Enumerations
  • Типы с плавающей запятой:
    • Single
    • Double
    • Extended
    • Currency
  • Object (экземпляр класса): Инструментарий имеет поддержку экземпляров классов, наследуемых от TBaseRemotable. TBaseRemotable базовый класс, используемый интерфейсом форматтера для возможностей настройки сериализации. Инструментарий обеспечивает класс TBaseComplexRemotable, который реализует сериализацию для их (или его наследников) опубликованных ("published") свойств.

Сериализация типа класса

Инструментарий имеет поддержку для экземпляров классов, наследуемых от TBaseRemotable. TBaseRemotable - абстрактный базовый класс, используемый интерфейсом класса для возможности настройки сериализации. Инструментарий обеспечивает класс TBaseComplexRemotable, который реализует сериализацию опубликованных (published) свойств для его классов-наследников. Также обеспечивается классом TBaseObjectArrayRemotable сериализация массивов классов-наследников TBaseRemotable.

Корневой класс “TBaseRemotable”
  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 это абстрактный базовый класс, используемый интерфейсом форматтера для возможностей настройки сериализации. Этот класс определяется виртуальный конструктором/virtual constructor и, главным образом, двумя виртуальными абстрактными методами класса:

  • Save: этот метод вызывается, когда инструментарию требуется сериализация параметра AObject.
  • Load: этот метод вызывается, когда инструментарию требуется де-сериализация параметра AObject.
Сериализация “TBaseComplexRemotable”
  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 реализует сериализацию для наследников опубликованных ("published") свойств классов-наследников. Сериализация основана на информации типов времени выполнения ("runtime type information" RTTI) и может быть настроена для:

  • всегда игноировать некоторых опубликованных свойства.
  • игнорирование на определенных условиях некоторых опубликованных свойств.

Нижеприведенный класс демонстрирует пример настройки сериализации.

  TSampleClass = class(TBaseComplexRemotable)
  private
    FProp_Always: Integer;
    FProp_Never: Integer;
    FProp_Optional: Integer;
    function GetStoredProp_Optional: boolean;
  published
    //Это свойство всегда будет сериализоваться
    property Prop_Always : Integer read FProp_Always write FProp_Always;
    //Это свойство никогда не будет сериализоваться
    property Prop_Never : Integer read FProp_Never write FProp_Never stored False;
    //Это свойство будет сериализоваться, если "Self.GetStoredProp_Optional() = True"
    property Prop_Optional : Integer read FProp_Optional write FProp_Optional stored GetStoredProp_Optional;
  End;
  • Свойства атрибутов

TBaseComplexRemotable позволяет сериализацию свойств как атрибутов. Свойства должны быть зарегистрированы в качестве таковых с помощью метода класса RegisterAttributeProperty или метода RegisterAttributeProperties.


TBaseComplexRemotable

TBaseComplexSimpleContentRemotable обеспечивает реализацию комплексных типов “XML Схемы”, которые расширяют простые типы атрибутами. Нижеследующий пример иллюстрирет это:

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

Этот тип будет приведен с помощью ws_helper к Pascal как

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

использование предустановленных типов ( в 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>
Provided array implementations

The toolkit provides array implementation for basic types ( in the base_service_intf unit ) listed below. 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():PTypeInfo;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.

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.

TODO

TODO Common

  • Simple type support in headers
  • Header support for the binary format
  • True Attribute serialization support for the binary format

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 (See Data Filters)
  • import functions from XMLRPC server via introspect http://scripts.incutio.com/xmlrpc/introspection.html

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

Лицензия

Весь код в WST под лицензией FPC' RTL (modified LGPL).

Загрузка и NewsGroup

FAQ

Вопросы и ответы по WST Web Service Toolkit:FAQ.

Журнал изменений

Version 0.6

  • License change :
    • All the code in WST is now using the FPC' RTL license (modified LGPL see COPYING.modifiedLGPL)
  • WSDL/XSD Parsers :
    • Referenced external schemes parsing : <import> and <include> handling
    • Pascal Unit renaming
    • Handle top level(global) declared attribute and references
    • More XSD constructs parsing
    • Parser case sensitivity can be enabled or disabled
    • Document wrapped parameters handling : generation of an easy access interface
  • Type Library Editor
    • Beautification : +images for menu items, +Tool bar
    • Support for External XSD Schema referencing
    • Documentation for objects
    • Show object dependency
    • Show object XSD schema
    • Collection based arrays support
    • Items cloning
    • Better WSDL generation
  • Run Time :
    • FPC's Native HTTP Client transport
    • FPC's Native TCP Client transport
    • FPC's Native HTTP Server
    • FPC's Native TCP Server
    • iOS http transport
    • Client Data Filter (interceptors used to manipulate the payload : encrypt, compress, ...)
    • Client side HTTP Cookie management
    • Better SOAP headers support
    • Better WSDL generation
  • Other :
    • Documentation updated : the sample used for the tutorial is now one of the WST samples
    • More test cases
    • Bug Fixes

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

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.

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.

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

Version 0.2.3.2 ( 5 July 2006 )

  • ICS and Indy HTTP Proxy "user" and "password" support.

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

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

Автор

Inoussa OUEDRAOGO, http://inoussa12.googlepages.com/

См. также

  • fcl-web FPC/Lazarus компоненты веб-сервера
  • [1] Статья о WST. Несмотря на то, что написана давно, но до сих пор может оказаться полезной для чтения