lazarus-ccr/wst/trunk/service_intf.pas
2011-03-01 15:42:53 +00:00

643 lines
18 KiB
ObjectPascal

{
This file is part of the Web Service Toolkit
Copyright (c) 2006 by Inoussa OUEDRAOGO
This file is provide under modified LGPL licence
( the files COPYING.modifiedLGPL and COPYING.LGPL).
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
{ Base service interface }
{$INCLUDE wst_global.inc}
unit service_intf;
interface
uses
Classes, SysUtils, TypInfo, Contnrs,
base_service_intf, wst_types;
Const
sTARGET = 'target';
Type
ICookieManager = interface
['{C04EFE37-A6BA-409E-9D9C-25836938858F}']
function GetCount() : Integer;
function GetName(const AIndex : Integer) : string;
function GetValue(const AIndex : Integer) : string; overload;
function GetValue(const AName : string) : string; overload;
procedure SetValue(const AIndex : Integer; const AValue : string); overload;
procedure SetValue(const AName : string; const AValue : string); overload;
end;
ITransport = Interface
['{AEB6677A-9620-4E7D-82A0-43E3C4C52B43}']
function GetTransportName() : string;
function GetPropertyManager():IPropertyManager;
procedure SendAndReceive(ARequest,AResponse:TStream);
function GetCookieManager() : ICookieManager;
End;
//The client formater interface, used to marshall parameters.
IFormatterClient = Interface(IFormatterBase)
['{73746BC7-CA43-4C00-8789-71E23033C3B2}']
procedure BeginCall(
const AProcName,
ATarget : string;
ACallContext : ICallContext
);
procedure EndCall();
procedure BeginCallRead(ACallContext : ICallContext);
function GetCallProcedureName():String;
function GetCallTarget():String;
End;
(* This interface is used with IFormatterClient to handle messages *)
ICallMaker = Interface
['{4CF7B98B-8C37-479F-AFF3-822FCCEECEC8}']
function GetPropertyManager():IPropertyManager;
procedure MakeCall(
ASerializer : IFormatterClient;
ATransport : ITransport
);
End;
(* A service protocol is defined by :
- a marshaller
- a call handler for that marshaller
- and a tranport. *)
IServiceProtocol = Interface
['{777FE102-0F6C-495C-9A92-528D07F1C60C}']
function GetSerializer() : IFormatterClient; // the marshaller >> SOAP, XML-RPC, Binary,...
function GetCallHandler() : ICallMaker; // Call handler >> SOAP call handler, XML-RPC call handler, ...
function GetTransport() : ITransport; // the transport >> HTTP, TCP, named pipes, ...
procedure SetTransport(AValue : ITransport);
End;
{ TBaseProxy }
(* The base class for service proxy *)
TBaseProxy = Class(TInterfacedObject,IInterface,ICallContext,IServiceProtocol)
private
FTarget : String;
FProtocol : IServiceProtocol;
FOperationsProperties : TStrings;
private
procedure LoadProperties();
protected
function GetTarget():String;{$IFDEF USE_INLINE}inline;{$ENDIF}
function GetSerializer() : IFormatterClient;
function GetCallHandler() : ICallMaker;
function GetTransport() : ITransport;
procedure SetTransport(AValue : ITransport);
procedure MakeCall();
class function GetServiceType() : PTypeInfo;virtual;abstract;
// ---- BEGIN >> ICallContext implementation ----
private
FCallContext : ICallContext;
protected
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;
// ---- END >> ICallContext implementation ----
procedure ClearHeaders(const ADirection : THeaderDirection);
function GetPropertyManager():IPropertyManager;
public
(* This is the primary constructor! *)
constructor Create(
Const ATarget : String; // the target service
Const AProtocol : IServiceProtocol
);overload;virtual;
(* A User friendly constructor *)
constructor Create(
Const ATarget : String;
Const AProtocolData : string;
Const ATransportData : string
);overload;virtual;
destructor Destroy();override;
End;
IFormaterQueryRegistry = Interface
['{037907E1-5E44-4A91-B290-CA70ACACF5E6}']
function Find(
Const AProtocolData : string;
Out ARes : IServiceProtocol
):Boolean;
procedure Register(
Const AProtocolName : string;
AFormaterFactory : IItemFactory;
ACallHandlerFactory : IItemFactory
);
End;
ITransportRegistry = Interface
['{ED34F7A2-2335-4FD3-A457-2B8C4349664E}']
function Find(
Const ATransportData : string;
Out ARes : ITransport
):Boolean;
procedure Register(
const ATransportName : string;
AFactory : IItemFactory
);
End;
function GetFormaterRegistry():IFormaterQueryRegistry;
function GetTransportRegistry():ITransportRegistry;
implementation
uses wst_consts,imp_utils, metadata_repository;
{ TBaseProxy }
procedure TBaseProxy.LoadProperties();
var
pd : PPropertyData;
i : Integer;
sd : PService;
opd : PServiceOperation;
mm : IModuleMetadataMngr;
strTransportBuffer, strFormatBuffer, strName : string;
begin
if not Assigned(FOperationsProperties) then begin
FOperationsProperties := TStringList.Create();
mm := GetModuleMetadataMngr();
sd := mm.GetServiceMetadata(GetTypeData(GetServiceType())^.IntfUnit,GetServiceType()^.Name);
try
Assert(Assigned(sd));
opd := sd^.Operations;
for i := 0 to Pred(sd^.OperationsCount) do begin
strFormatBuffer := '';
strTransportBuffer := '';
pd := opd^.Properties;
while Assigned(pd) do begin
strName := ExtractOptionName(pd^.Name);
if ( AnsiPos(sFORMAT + '_',pd^.Name) = 1 ) then begin
if ( Length(strName) > 0 ) then begin
strFormatBuffer := Format('%s%s=%s;',[strFormatBuffer,strName,pd^.Data]);
end;
end else if ( AnsiPos(sTRANSPORT + '_',pd^.Name) = 1 ) then begin
if ( Length(strName) > 0 ) then begin
strTransportBuffer := Format('%s%s=%s;',[strTransportBuffer,strName,pd^.Data]);
end;
end;
pd := pd^.Next;
end;
if not IsStrEmpty(strFormatBuffer) then begin
Delete(strFormatBuffer,Length(strFormatBuffer),1);
FOperationsProperties.Values[opd^.Name + '_' + sFORMAT] := strFormatBuffer;
end;
if not IsStrEmpty(strTransportBuffer) then begin
Delete(strTransportBuffer,Length(strTransportBuffer),1);
FOperationsProperties.Values[opd^.Name + '_' + sTRANSPORT] := strTransportBuffer;
end;
Inc(opd);
end;
finally
mm.ClearServiceMetadata(sd);
end;
end;
end;
function TBaseProxy.GetTarget(): String;
begin
Result := FTarget;
end;
function TBaseProxy.GetSerializer(): IFormatterClient;
begin
Result := FProtocol.GetSerializer();
end;
function TBaseProxy.GetCallHandler(): ICallMaker;
begin
Result := FProtocol.GetCallHandler();
end;
function TBaseProxy.GetTransport(): ITransport;
begin
Result := FProtocol.GetTransport();
end;
procedure TBaseProxy.SetTransport(AValue : ITransport);
begin
FProtocol.SetTransport(AValue);
end;
procedure TBaseProxy.MakeCall();
var
trans : ITransport;
frmt : IFormatterClient;
procedure PrepareCall();
var
strBuffer, strName : string;
begin
LoadProperties();
strName := frmt.GetCallProcedureName() + '_';
strBuffer := FOperationsProperties.Values[strName + sTRANSPORT];
if not IsStrEmpty(strBuffer) then
trans.GetPropertyManager().SetProperties(strBuffer);
strBuffer := FOperationsProperties.Values[strName + sFORMAT];
if not IsStrEmpty(strBuffer) then
frmt.GetPropertyManager().SetProperties(strBuffer);
end;
begin
trans := GetTransport();
frmt := GetSerializer();
PrepareCall();
GetCallHandler().MakeCall(frmt,trans);
end;
procedure TBaseProxy.AddObjectToFree(const AObject: TObject);
begin
FCallContext.AddObjectToFree(AObject);
end;
procedure TBaseProxy.Clear();
begin
FCallContext.Clear();
end;
function TBaseProxy.AddHeader(
const AHeader: THeaderBlock;
const AKeepOwnership: Boolean
): Integer;
begin
Result := FCallContext.AddHeader(AHeader,AKeepOwnership);
end;
function TBaseProxy.AddHeader(
const AHeader : TBaseRemotable;
const AKeepOwnership : Boolean;
const AName : string = ''
): Integer;
begin
Result := FCallContext.AddHeader(AHeader,AKeepOwnership,AName);
end;
function TBaseProxy.GetHeaderCount(const ADirections : THeaderDirections):Integer;
begin
Result := FCallContext.GetHeaderCount(ADirections);
end;
function TBaseProxy.GetHeader(const AIndex: Integer): THeaderBlock;
begin
Result := FCallContext.GetHeader(AIndex);
end;
procedure TBaseProxy.ClearHeaders(const ADirection: THeaderDirection);
begin
FCallContext.ClearHeaders(ADirection);
end;
function TBaseProxy.GetPropertyManager(): IPropertyManager;
begin
Result := FCallContext.GetPropertyManager();
end;
constructor TBaseProxy.Create(
const ATarget : String;
const AProtocol : IServiceProtocol
);
begin
if ( AProtocol = nil ) then
raise EServiceConfigException.CreateFmt(SERR_InvalidParameter,['AProtocol']);
if ( AProtocol.GetCallHandler() = nil ) then
raise EServiceConfigException.CreateFmt(SERR_InvalidParameter,['AProtocol.GetCallHandler()']);
if ( AProtocol.GetSerializer() = nil ) then
raise EServiceConfigException.CreateFmt(SERR_InvalidParameter,['AProtocol.GetSerializer()']);
if ( AProtocol.GetTransport() = nil ) then
raise EServiceConfigException.CreateFmt(SERR_InvalidParameter,['AProtocol.GetTransport()']);
FCallContext := TSimpleCallContext.Create() as ICallContext;
FTarget := ATarget;
FProtocol := AProtocol;
FProtocol.GetSerializer().GetPropertyManager().SetProperty(sTARGET,FTarget);
FProtocol.GetCallHandler().GetPropertyManager().SetProperty(sTARGET,FTarget);
end;
constructor TBaseProxy.Create(
const ATarget: string;
const AProtocolData: string;
const ATransportData: string
);
var
ptcl : IServiceProtocol;
tmpTrprt : ITransport;
begin
if not GetFormaterRegistry().Find(AProtocolData,ptcl) then
raise EServiceConfigException.CreateFmt(SERR_InvalidParameter,['AProtocolData']);
if not GetTransportRegistry().Find(ATransportData,tmpTrprt) then
raise EServiceConfigException.CreateFmt(SERR_InvalidParameter,['ATransportData']);
ptcl.SetTransport(tmpTrprt);
Create(ATarget,ptcl);
end;
destructor TBaseProxy.Destroy();
begin
FProtocol := Nil;
FreeAndNil(FOperationsProperties);
inherited Destroy();
end;
Const PROTOCOL_SEPARATOR = ':';
function ExtractProtocol( Const AProtocolName : string):String;
Var
i : Integer;
begin
i := Pos(PROTOCOL_SEPARATOR,AProtocolName);
If ( i <= 0 ) Then
i := MaxInt;
Result := lowercase(Copy(AProtocolName,1,Pred(i)));
end;
function ExtractProtocolData(Const AProtocolPropsStr : string):String;
Var
i : Integer;
begin
i := Pos(PROTOCOL_SEPARATOR,AProtocolPropsStr);
If ( i <= 0 ) Then
i := 0;
Result := Copy(AProtocolPropsStr,Succ(i),MaxInt);
end;
Type
{ TFormatterFactoryRegistryItem }
{ TServiceProtocol }
TServiceProtocol = class(TInterfacedObject,IInterface,IServiceProtocol)
Private
FFormatter : IFormatterClient;
FCallHandler : ICallMaker;
FTransport : ITransport;
Protected
function GetSerializer() : IFormatterClient;
function GetCallHandler() : ICallMaker;
function GetTransport() : ITransport;
procedure SetTransport(AValue : ITransport);
Public
constructor Create(
AFormatter : IFormatterClient;
ACallHandler : ICallMaker
);
destructor Destroy();override;
End;
TFormatterFactoryRegistryItem = class
private
FCallHandlerFactory: IItemFactory;
FFormaterFactory: IItemFactory;
FProtocolName: string;
public
constructor Create(
Const AProtocolName : string;
AFormaterFactory : IItemFactory;
ACallHandlerFactory : IItemFactory
);
destructor Destroy();override;
property ProtocolName : string Read FProtocolName;
property FormaterFactory : IItemFactory Read FFormaterFactory;
property CallHandlerFactory : IItemFactory Read FCallHandlerFactory;
End;
{ TFormatterRegistry }
//Make it Threadsafe ???
TFormatterRegistry = class(TInterfacedObject,IInterface,IFormaterQueryRegistry)
private
FList : TObjectList;
function IndexOf(Const AName : string ):Integer;
//procedure Clear();
function GetCount():Integer;
function GetItem(const AIndex:Integer): TFormatterFactoryRegistryItem;
protected
function Find(
Const AProtocolData : string;
Out ARes : IServiceProtocol
):Boolean;
procedure Register(
Const AProtocolName : string;
AFormaterFactory : IItemFactory;
ACallHandlerFactory : IItemFactory
);
public
constructor Create();
destructor Destroy();override;
End;
{ TServiceProtocol }
function TServiceProtocol.GetSerializer(): IFormatterClient;
begin
Result := FFormatter;
end;
function TServiceProtocol.GetCallHandler(): ICallMaker;
begin
Result := FCallHandler;
end;
function TServiceProtocol.GetTransport(): ITransport;
begin
Result := FTransport;
end;
procedure TServiceProtocol.SetTransport(AValue: ITransport);
begin
FTransport := AValue;
end;
constructor TServiceProtocol.Create(AFormatter: IFormatterClient;ACallHandler: ICallMaker);
begin
FFormatter := AFormatter;
FCallHandler := ACallHandler;
end;
destructor TServiceProtocol.Destroy();
begin
FFormatter := nil;
FCallHandler := nil;
FTransport := nil;
inherited;
end;
{ TFormatterFactoryRegistryItem }
constructor TFormatterFactoryRegistryItem.Create(
const AProtocolName: string;
AFormaterFactory: IItemFactory; ACallHandlerFactory: IItemFactory);
begin
FProtocolName := AProtocolName;
FFormaterFactory := AFormaterFactory;
FCallHandlerFactory := ACallHandlerFactory;
end;
destructor TFormatterFactoryRegistryItem.Destroy();
begin
FFormaterFactory := nil;
FCallHandlerFactory := nil;
inherited Destroy();
end;
Var
FormaterRegistryInst : IFormaterQueryRegistry = Nil;
function GetFormaterRegistry():IFormaterQueryRegistry;
begin
If Not Assigned(FormaterRegistryInst) Then
FormaterRegistryInst := TFormatterRegistry.Create() as IFormaterQueryRegistry;// Lock!!!
Result := FormaterRegistryInst;
end;
{ TFormatterRegistry }
function TFormatterRegistry.IndexOf(const AName: string): Integer;
Var
s : string;
begin
s := lowercase(AName);
For Result := 0 To Pred(GetCount()) Do
If SameText(s,GetItem(Result).ProtocolName) Then
Exit;
Result := -1;
end;
{procedure TFormatterRegistry.Clear();
begin
FList.Clear();
end;}
function TFormatterRegistry.GetCount(): Integer;
begin
Result := FList.Count;
end;
function TFormatterRegistry.GetItem(const AIndex: Integer): TFormatterFactoryRegistryItem;
begin
Result := FList[AIndex] as TFormatterFactoryRegistryItem;
end;
function TFormatterRegistry.Find(
Const AProtocolData : string;
Out ARes : IServiceProtocol
): Boolean;
Var
i : Integer;
r : TFormatterFactoryRegistryItem;
initData : String;
begin
ARes := Nil;
i := IndexOf(ExtractProtocol(AProtocolData));
Result := ( i > -1 );
If Result Then Begin
initData := ExtractProtocolData(AProtocolData);
r := GetItem(i);
ARes := TServiceProtocol.Create(
r.FormaterFactory.CreateInstance() as IFormatterClient,
r.CallHandlerFactory.CreateInstance() as ICallMaker
) as IServiceProtocol;
ARes.GetSerializer().GetPropertyManager().SetProperties(initData);
ARes.GetCallHandler().GetPropertyManager().SetProperties(initData);
End;
end;
procedure TFormatterRegistry.Register(
Const AProtocolName : string;
AFormaterFactory : IItemFactory;
ACallHandlerFactory : IItemFactory
);
Var
i : Integer;
s : string;
begin
Assert(Assigned(AFormaterFactory));
Assert(Assigned(ACallHandlerFactory));
s := ExtractProtocol(AProtocolName);
i := IndexOf(s);
If ( i = -1 ) Then
FList.Add(TFormatterFactoryRegistryItem.Create(s,AFormaterFactory,ACallHandlerFactory));
end;
constructor TFormatterRegistry.Create();
begin
FList := TObjectList.Create(True);
end;
destructor TFormatterRegistry.Destroy();
begin
FreeAndNil(FList);
inherited Destroy();
end;
Type
{ TTransportRegistry }
//Make it Threadsafe ???
TTransportRegistry = class(TBaseFactoryRegistry,IInterface,ITransportRegistry)
protected
function Find(
Const ATransportData : string;
Out ARes : ITransport
):Boolean;
End;
Var
TransportRegistryInst : ITransportRegistry = Nil;
function GetTransportRegistry():ITransportRegistry;
begin
If Not Assigned(TransportRegistryInst) Then
TransportRegistryInst := TTransportRegistry.Create() as ITransportRegistry;// Lock!!!
Result := TransportRegistryInst;
end;
{ TTransportRegistry }
function TTransportRegistry.Find(
const ATransportData : string;
Out ARes : ITransport
): Boolean;
Var
fct : IItemFactory;
begin
fct := FindFactory(ExtractProtocol(ATransportData));
If Assigned(fct) Then Begin
ARes := fct.CreateInstance() as ITransport;
ARes.GetPropertyManager().SetProperties(ExtractProtocolData(ATransportData));
Result := True;
End Else Begin
Result := False;
End;
end;
initialization
TransportRegistryInst := TTransportRegistry.Create() as ITransportRegistry;
FormaterRegistryInst := TFormatterRegistry.Create() as IFormaterQueryRegistry;
finalization
FormaterRegistryInst := nil;
TransportRegistryInst := nil;
end.