xtool/contrib/mORMot/SQLite3/DDD/dom/asynch.pas.mustache

541 lines
18 KiB
Plaintext

{{#soa}}
{{#unitasynch}}
/// asynch version of {{#services}}{{interfaceName}} {{/services}}
{{/unitasynch}}
{{#unitsynch}}
/// implements {{#services}}{{interfaceName}} {{/services}}over *Asynch
{{/unitsynch}}
unit {{filename}};
{
WARNING:
This unit has been generated by {{exeName}}.
Any manual modification of this file may be lost after regeneration.
{{#unitasynch}}
Defines asynchronous (non-blocking) types for the following services:
{{#services}}
- {{interfaceName}} as non-blocking {{interfaceName}}Asynch,
associated with blocking T{{uri}}Synch / {{interfaceName}}Synch,
{{interfaceName}}AsynchAck and T{{uri}}Delays.
{{/services}}
{{/unitasynch}}
{{#unitsynch}}
Defines synchronous (blocking) implementation for the following services:
{{#services}}
- {{interfaceName}} as blocking T{{uri}}Abstract,
calling {{interfaceName}}Synch / {{interfaceName}}Asynch
{{/services}}
{{/unitsynch}}
Corresponding to {{projectname}} version {{exeVersion}}.
Generated by {{User}} at {{time}}.
}
{{<callparam}}const call: {{calltype}}{{/callparam}}
interface
{{<asynchparam}}{{#asynchkey}}const {{.}}: {{asynchkeytype}}{{/asynchkey}}{{/asynchparam}}
uses
SysUtils,
SynCommons,
SynLog,
mORMot,
{{#units}}
{{.}},
{{/units}}
mORMotDDD;
{{<methodasynch}}{{methodName}}({{>asynchparam}}{{#args}}{{#dirInput}};
{{dirName}} {{argName}}: {{typeSource}}{{/dirInput}}{{/args}};
{{>callparam}}){{#args}}{{#dirResult}}: {{typeSource}}{{/dirResult}}{{/args}};{{/methodasynch}}
{{<methodack}}{{methodName}}({{>callparam}}; {{#args}}{{^dirResult}}{{#dirOutput}}
const {{argName}}: {{typeSource}};{{/dirOutput}}{{/dirResult}}{{/args}}{{#args}}{{#dirResult}}
const res: {{typeSource}}{{/dirResult}}{{/args}});{{/methodack}}
{{<methodsynch}}{{methodName}}({{>asynchparam}}{{#args}}{{^dirResult}};
{{dirName}} {{argName}}: {{typeSource}}{{/dirResult}}{{/args}}){{#args}}{{#dirResult}}: {{typeSource}}{{/dirResult}}{{/args}};{{/methodsynch}}
{{#services}}{{#unitasynch}}
{ -------- asynchronous version of {{interfaceName}} }
type
{{<methoddelay}}{{#asynchdelay}}{{.}}{{/asynchdelay}}{{^asynchdelay}}{{defaultdelay}}{{/asynchdelay}}{{/methoddelay}}
/// settings associated to {{interfaceName}}Asynch timeouts
T{{uri}}Delays = class(TSynPersistent)
protected
{{#methods}}
{{^isInherited}}
f{{methodName}}: integer;
{{/isInherited}}
{{/methods}}
public
/// would set all delays to their default values
constructor Create; override;
published
{{#methods}}
{{^isInherited}}
/// default delay for {{interfaceName}}Asynch.{{methodName}} is {{>methoddelay}} ms
property {{methodName}}: integer read f{{methodName}} write f{{methodName}};
{{/isInherited}}
{{/methods}}
end;
/// the {{interfaceName}}Asynch progress callback definition
// - a single callback, after subscription via Subscribe{{uri}}(),
// would receive the acknowledgements of all {{interfaceName}}Asynch methods
// - some commands may take a lot of time, so this asynchronous mechanism
// would increase the system reactivity
// - naming is following the {{interfaceName}} method names
// - call: {{calltype}} is the opaque value supplied at command invoke
{{interfaceName}}AsynchAck = interface(IInvokable)
['{{newguid .}}']
{{#methods}}
{{^isInherited}}
procedure {{>methodack}}
{{/isInherited}}
{{/methods}}
end;
/// identify any {{interfaceName}}Asynch method
// - see also ToText(), ToMethodName() and To{{uri}}Ack() functions
T{{uri}}Ack = (
ack{{uri}}Undefined{{#methods}}{{^isInherited}},
ack{{methodName}}{{/isInherited}}{{/methods}});
/// high-level asynchronous (non blocking) definition of {{interfaceName}}
// - all the methods match the latest inheritance level of synchronous
// (blocking) {{interfaceName}} - it won't define the parents methods,
// since it would allow to work on a dual phase Select/Command with no
// prior Select (multiple inheritance of interfaces may have helped a lot, but
// but they are not allowed yet){{#asynchkey}} using {{.}}: {{asynchkeytype}} to redirect
// the {{interfaceName}}Asynch call to the corresponding {{interfaceName}}
{{/asynchkey}} // - call: {{calltype}} is an opaque value, which would identify the command
// when it is acknowledged by {{interfaceName}}AsynchAck
{{interfaceName}}Asynch = interface(IInvokable)
['{{newguid .}}']
/// this method is expected to be called once at the beginning of the
// process, to receive all asynchronous acknowledgements of the other methods
// - it would return the default delays for the associated timeouts, as
// defined on the server side
function Subscribe{{uri}}(const OnAck: {{interfaceName}}AsynchAck;
out Delays: T{{uri}}Delays): TCQRSResult;
// all methods below map {{interfaceName}} methods, and their input parameters
{{#methods}}
{{^isInherited}}
{{verb}} {{>methodasynch}}
{{/isInherited}}
{{/methods}}
end;
/// waiting semaphore associated to {{interfaceName}}Asynch
// - used internally by T{{uri}}AsynchAck
T{{uri}}AsynchCall = class(TBlockingProcessPoolItem)
protected
procedure ResetInternal; override; // set Params to 0
public
Params: record
// execution context
{{#asynchkey}}
{{.}}: {{asynchkeytype}};
{{/asynchkey}}
methodname: RawUTF8;
ack: T{{uri}}Ack;
// additional parameters, copied from {{interfaceName}}AsynchAck
res: TCQRSResult;{{#methods}}{{^isInherited}}{{#args}}{{#dirOutput}}{{^dirResult}}
{{argName}}{{methodIndex}}: {{typeSource}};{{/dirResult}}{{/dirOutput}}{{/args}}{{/isInherited}}{{/methods}}
end;
published
{{#asynchkey}}
property {{.}}: {{asynchkeytype}} read Params.{{.}};
{{/asynchkey}}
property ack: T{{uri}}Ack read Params.ack;
property res: TCQRSResult read Params.res;
end;
/// propagate acknowledgements for {{interfaceName}}Asynch
// - {{interfaceName}}AsynchAck acknowledgements would be propagated using the
// associated {{calltype}}, to release the wait of the main {{interfaceName}}
// blocking process
// - would allow to run {{interfaceName}} blocking methods over a supplied
// {{interfaceName}}Asynch instance
T{{uri}}AsynchAck = class(TCQRSServiceAsynchAck, {{interfaceName}}AsynchAck)
protected
function Notify({{>callparam}}; ack: T{{uri}}Ack;
res: TCQRSResult; out process: T{{uri}}AsynchCall): boolean; overload;
procedure Notify({{>callparam}}; ack: T{{uri}}Ack;
res: TCQRSResult); overload;
// {{interfaceName}}AsynchAck methods
// would propagate the acknowledgement, and copy any additional parameter
// to T{{uri}}AsynchCall.Params
{{#methods}}
{{^isInherited}}
procedure {{>methodack}}
{{/isInherited}}
{{/methods}}
public
constructor Create(aLog: TSynLogClass);
/// returns a blocking process from the internal semaphore pool
function NewAsynchCall: T{{uri}}AsynchCall;
end;
/// shared synchronous (blocking) interface of {{interfaceName}}Asynch
{{#asynchkey}}
// - every method expects a {{.}}: {{asynchkeytype}} first input
// parameter, in addition to the regular {{interfaceName}} parameters
{{/asynchkey}}
{{interfaceName}}Synch = interface(IInvokable)
['{{newguid .}}']
{{#methods}}
{{^isInherited}}
{{verb}} {{>methodsynch}}
{{/isInherited}}
{{/methods}}
end;
/// implements {{interfaceName}}Synch over a {{interfaceName}}Asynch instance
// - it will use a shared T{{uri}}AsynchAck callback to wait for each
// command to be finished, and emulate synchronous (non-blocking) execution
// - you may use this class e.g. at API level, over a blocking REST server,
// and communicate with the Domain event-driven services via asynchronous calls
T{{uri}}Synch = class(TCQRSServiceSynch, {{interfaceName}}Synch)
protected
fLog: TSynLogClass;
fDelays: T{{uri}}Delays;
fDelaysOwned: boolean;
fAsynch: {{interfaceName}}Asynch;
fSharedCallback: T{{uri}}AsynchAck;
procedure WaitFor(call: T{{uri}}AsynchCall;{{#asynchkey}} const {{.}}: {{asynchkeytype}};{{/asynchkey}}
delay: integer; ack: T{{uri}}Ack; var result: TCQRSResult);
public
/// initialize the blocking instance
// - would allocate an internal T{{uri}}AsynchAck callback, and
// execute {{interfaceName}}Asynch.Subscribe{{uri}}()
// - you may specify custom delays, to overload values supplied by the server
// during Subscribe{{uri}}()
constructor Create(const aAsynch: {{interfaceName}}Asynch;
aDelays: T{{uri}}Delays = nil; aLog: TSynLogClass = nil); reintroduce;
/// finalize the instance
destructor Destroy; override;
/// access to the asynchronous methods
property Asynch: {{interfaceName}}Asynch read fAsynch;
/// associated time out values, in ms
property Delays: T{{uri}}Delays read fDelays;
public
// {{interfaceName}}Synch blocking methods, returning cqrsTimeout if the
// non-blocking calls did not respond in the expected delay, or the
// TCQRSResult returned by the associated {{interfaceName}}Asynch method
{{#methods}}
{{^isInherited}}
{{verb}} {{>methodsynch}}
{{/isInherited}}
{{/methods}}
end;
/// returns the low-level text value of the enumerated, including trailing "ack"
// - may be used e.g. for debugging/logging purpose
function ToText(ack: T{{uri}}Ack): PShortString; overload;
/// returns the original method name without trailing "ack", as defined in
// {{interfaceName}}Asynch
// - reverse function of To{{uri}}Ack()
function ToMethodName(ack: T{{uri}}Ack): RawUTF8; overload;
/// find a T{{uri}}Ack item, matching original method name
// without trailing "ack", as defined in {{interfaceName}}Asynch
// - reverse function of ToMethodName()
function To{{uri}}Ack(const MethodName: RawUTF8): T{{uri}}Ack;
{{/unitasynch}}
{{#asynchkey}}{{#unitsynch}}{ -------- implements {{interfaceName}} over {{interfaceName}}Synch }
{{#query}}{{<method}}{{methodName}}({{#args}}{{^dirResult}}
{{dirName}} {{argName}}: {{typeSource}}{{commaArg}}{{/dirResult}}{{/args}}){{#args}}{{#dirResult}}: {{typeSource}}{{/dirResult}}{{/args}};{{/method}}
type
/// implements CQRS two-phase commit over a {{interfaceName}}Asynch instance
// - first Select phase should have been implemented in {{.}}
// - expects a f{{asynchkey}}: {{asynchkeytype}} field to be available,
// so that the proper {{interfaceName}}Synch method would be called
// - this abstract class should be inherited, and override Set{{uri}}Synch
T{{uri}}Abstract = class({{.}}, {{interfaceName}})
protected
f{{uri}}Synch: {{interfaceName}}Synch;
function BeginSynch(var aResult: TCQRSResult): boolean;
// should be overriden, to set f{{uri}}Synch from f{{asynchkey}}
procedure Set{{uri}}Synch; virtual; abstract;
public
// {{interfaceName}} blocking methods
{{#methods}}
{{^isInherited}}
{{verb}} {{>method}}
{{/isInherited}}
{{/methods}}
end;
{{/query}}{{/unitsynch}}{{/asynchkey}}
{{/services}}
implementation
{{#services}}
{{#unitasynch}}
{ -------- asynchronous version of {{interfaceName}} }
function ToText(ack: T{{uri}}Ack): PShortString;
begin
result := GetEnumName(TypeInfo(T{{uri}}Ack), ord(ack));
end;
function ToMethodName(ack: T{{uri}}Ack): RawUTF8;
begin
result := TrimLeftLowerCaseShort(ToText(ack));
end;
function To{{uri}}Ack(const MethodName: RawUTF8): T{{uri}}Ack;
var
ndx: integer;
begin
ndx := GetEnumNameValueTrimmed(TypeInfo(T{{uri}}Ack),
pointer(MethodName), length(MethodName));
if ndx > 0 then
result := T{{uri}}Ack(ndx)
else
result := ack{{uri}}Undefined;
end;
{ T{{uri}}Delays }
constructor T{{uri}}Delays.Create;
begin
inherited;
{{#methods}}
{{^isInherited}}
f{{methodName}} := {{>methoddelay}};
{{/isInherited}}
{{/methods}}
end;
{ T{{uri}}AsynchCall }
procedure T{{uri}}AsynchCall.ResetInternal;
begin
inherited ResetInternal; // set fEvent := evNone and fCall := 0
Finalize(Params);
FillCharFast(Params, sizeof(Params), 0);
end;
{ T{{uri}}AsynchAck }
constructor T{{uri}}AsynchAck.Create(aLog: TSynLogClass);
begin
inherited Create;
fLog := aLog;
fCalls := TBlockingProcessPool.Create(T{{uri}}AsynchCall);
end;
{{<callfmt}}%(call=%,{{#asynchkey}}%,{{/asynchkey}}%){{/callfmt}}
function T{{uri}}AsynchAck.Notify({{>callparam}};
ack: T{{uri}}Ack; res: TCQRSResult; out process: T{{uri}}AsynchCall): boolean;
var
id: integer;
begin
result := false;
{{#callfunction}}
if not {{.}}(call, id) then begin
fLog.Add.Log(sllTrace, 'Notify: invalid %(call=%) received', [ToText(ack)^, call], self);
exit;
end;
{{/callfunction}}
{{^callfunction}}
id := call;
{{/callfunction}}
process := pointer(fCalls.FromCall(id, true));
if process = nil then begin
fLog.Add.Log(sllTrace, 'Notify: deprecated/unexpected {{>callfmt}} received -> skipped',
[ToText(ack)^, id, {{#asynchkey}}'?', {{/asynchkey}}ToText(res)^], self);
exit;
end;
fLog.Add.Log(sllTrace, 'Notify: {{>callfmt}} received',
[process.Params.methodname, id, {{#asynchkey}}process.{{.}}, {{/asynchkey}}ToText(res)^], self);
process.Params.res := res;
result := true;
end;
procedure T{{uri}}AsynchAck.Notify({{>callparam}};
ack: T{{uri}}Ack; res: TCQRSResult);
var
process: T{{uri}}AsynchCall;
begin
if Notify(call, ack, res, process) then
process.NotifyFinished(true); // notify caller to unlock "WaitFor" method
end;
function T{{uri}}AsynchAck.NewAsynchCall: T{{uri}}AsynchCall;
begin
result := pointer(fCalls.NewProcess(0));
if result = nil then
raise {{Exception}}.CreateUTF8('%.NewAsynchCall: NewProcess=nil', [self]);
end;
// {{interfaceName}}AsynchAck methods
{{#methods}}
{{^isInherited}}
procedure T{{uri}}AsynchAck.{{>methodack}}
{{#hasOutNotResultParams}}
var
process: T{{uri}}AsynchCall;
begin
if Notify(call, ack{{methodName}}, res, process) then begin{{#args}}{{#dirOutput}}{{^dirResult}}
process.Params.{{argName}}{{methodIndex}} := {{argName}};{{/dirResult}}{{/dirOutput}}{{/args}}
process.NotifyFinished(true);
end;
{{/hasOutNotResultParams}}
{{^hasOutNotResultParams}}
begin
Notify(call, ack{{methodName}}, res);
{{/hasOutNotResultParams}}
end;
{{/isInherited}}
{{/methods}}
{ T{{uri}}Synch }
constructor T{{uri}}Synch.Create(const aAsynch: {{interfaceName}}Asynch;
aDelays: T{{uri}}Delays; aLog: TSynLogClass);
var
res: TCQRSResult;
outdelays: T{{uri}}Delays;
begin
if aAsynch = nil then
raise {{exception}}.CreateUTF8('%.Create(aAsynch=nil)', [self]);
fAsynch := aAsynch;
fLog := aLog;
fSharedCallback := T{{uri}}AsynchAck.Create(fLog);
inherited Create(fSharedCallback);
outdelays := T{{uri}}Delays.Create;
try
res := fAsynch.Subscribe{{uri}}(fSharedCallback, outdelays);
if res <> cqrsSuccess then
raise EDomPanel.CreateUTF8('%.Create: {{interfaceName}}Asynch.Subscribe=%',
[self, ToText(res)^]);
if aDelays <> nil then
fDelays := aDelays // force custom delays
else begin
fDelays := outdelays;
fDelaysOwned := true;
outdelays := nil;
end;
finally
outdelays.Free;
end;
end;
destructor T{{uri}}Synch.Destroy;
begin
if fDelaysOwned then
fDelays.Free;
inherited Destroy;
end;
procedure T{{uri}}Synch.WaitFor(call: T{{uri}}AsynchCall;
{{#asynchkey}}const {{.}}: {{asynchkeytype}}; {{/asynchkey}}delay: integer; ack: T{{uri}}Ack;
var result: TCQRSResult);
var
msg: RawUTF8;
begin
call.Lock;
try
{{#asynchkey}}
call.Params.{{.}} := {{.}}; // for Notify()
{{/asynchkey}}
call.Params.ack := ack;
call.Params.methodname := ToMethodName(ack);
FormatUTF8('WaitFor: Asynch.{{>callfmt}}',
[call.Params.methodname, call.Call, {{#asynchkey}}{{.}}, {{/asynchkey}}ToText(result)^], msg);
finally
call.Unlock;
end;
fLog.Add.Log(sllTrace, msg, self);
if result <> cqrsSuccess then
fLog.Add.Log(sllDDDError, '%: input parameters?', [msg])
else if call.WaitFor(delay) = evTimeOut then begin
fLog.Add.Log(sllDDDInfo, '% timeout after %ms', [msg, delay]);
result := cqrsTimeout;
end
else
result := call.Params.res;
end;
// {{interfaceName}}Synch blocking methods
{{<argvalue}}{{#isEnum}}ToText({{argName}})^{{/isEnum}}{{^isEnum}}{{argName}}{{/isEnum}}{{/argvalue}}
{{#methods}}
{{^isInherited}}
{{verb}} T{{uri}}Synch.{{>methodsynch}}
var
log: ISynLog;
call: T{{uri}}AsynchCall;
begin
if fLog <> nil then
log := fLog.Enter('{{methodName}}({{#asynchkey}}{{.}}=%{{/asynchkey}}{{#args}}{{#dirInput}}, {{argName}}=%{{/dirInput}}{{/args}})',
[{{#asynchkey}}{{.}}{{/asynchkey}}{{#args}}{{#dirInput}},{{>argvalue}}{{/dirInput}}{{/args}}], self);
try
call := fSharedCallback.NewAsynchCall;
try
result := Asynch.{{methodName}}({{#asynchkey}}{{.}}, {{/asynchkey}}{{#args}}{{#dirInput}}{{argName}}, {{/dirInput}}{{/args}}call.Call);
WaitFor(call, {{#asynchkey}}{{.}}, {{/asynchkey}}Delays.{{methodName}}, ack{{methodName}}, result);
finally{{#hasOutNotResultParams}}{{#args}}{{#dirOutput}}{{^dirResult}}
{{argName}} := call.Params.{{argName}}{{methodIndex}};{{/dirResult}}{{/dirOutput}}{{/args}}{{/hasOutNotResultParams}}
call.Reset;
end;
except
on Exception do
result := cqrsInternalError;
end;
if log <> nil then
log.Log(sllDebug, '{{methodName}}{{#asynchkey}}(%){{/asynchkey}} returned %{{#args}}{{#dirOutput}}{{^dirResult}} {{argName}}=%{{/dirResult}}{{/dirOutput}}{{/args}}',
[{{#asynchkey}}{{.}}, {{/asynchkey}}ToText(result)^{{#args}}{{#dirOutput}}{{^dirResult}}, {{>argvalue}}{{/dirResult}}{{/dirOutput}}{{/args}}], self);
end;
{{/isInherited}}
{{/methods}}
{{/unitasynch}}
{{#asynchkey}}{{#unitsynch}}{ -------- implements {{interfaceName}} over {{interfaceName}}Synch }
{{#query}}
{ T{{uri}}Abstract }
function T{{uri}}Abstract.BeginSynch(var aResult: TCQRSResult): boolean;
begin
result := false;
if CqrsBeginMethod(qaCommandOnSelect, aResult) then begin
Set{{uri}}Synch;
if f{{uri}}Synch = nil then
CqrsSetResultMsg(cqrsInternalError, '{{uri}}Synch=nil')
else
result := true;
end;
end;
{{#methods}}{{^isInherited}}
{{verb}} T{{uri}}Abstract.{{>method}}
begin
if BeginSynch(result) then
CqrsSetResult(f{{uri}}Synch.{{methodName}}(
f{{asynchkey}}{{#args}}{{^dirResult}}, {{argName}}{{/dirResult}}{{/args}}));
end;
{{/isInherited}}
{{/methods}}
{{/query}}{{/unitsynch}}{{/asynchkey}}
{{/services}}
initialization
{{#services}}
{{#unitasynch}}
TInterfaceFactory.RegisterInterfaces([
TypeInfo({{interfaceName}}AsynchAck), TypeInfo({{interfaceName}}Asynch)]);
{{/unitasynch}}
{{/services}}
{{/soa}}
end.