#1 2016-12-09 08:19:10

EvaF
Member
Registered: 2014-07-19
Posts: 40

SMS - SynCrossPlatformREST-Queue of requests

I use Smart Mobile Studio to communicate with mORMot server (based on RegressionTestsServer). For my project the most suitable method is to use the sicPerSession implementation with asynchronous responses. More precisely - I need to put my requests into a FIFO queue (without waiting for each response) and let the mormot client manage the queue . 
I have looked at the SynCrossPlatformREST unit,  but didn't find the option how to implement such approach (I may be wrong)

So here are my questions (for the implementation sicPerSession):
1) is it possible to ensure  that the request  is delayed until the server completes the processing of the previous request?
2) I tried to implement simple queueing in SynCrossPlatformREST unit. It works but I wonder if there is  a more efficient way to achive it?

  TCallQueue=class                                                  <---added
    Caller: TServiceClientAbstract;
    CallMethod: string;
    CallParams: string;
    onSuccess: procedure(res: array of Variant);
    onError: TSQLRestEvent;
    ReturnsCustomAnswer: boolean;
    ExpectedOutputParamsCount : integer;
  end;

  /// REST client access class
  TSQLRestClientURI = class(TSQLRest)
  protected
    fAuthentication: TSQLRestServerAuthentication;
    fOnlyJSONRequests: boolean;
    fRunningClientDriven: TStringList;
    {$ifdef ISSMS}
    fAsynchCount: integer;
    fCallsQueue:  Array of TCallQueue;                            <---added 
    fCallIsProcessed: boolean;                                     <---added

    fAsynchPendingText: array of string;
    procedure SyncCallRemoteServiceASynchQueue;                  <---added
    ...
  public
    ...
    procedure CallRemoteServiceAsynch(aCaller: TServiceClientAbstract;
      const aMethodName: string; aExpectedOutputParamsCount: integer;
      const aInputParams: array of variant; 
      onSuccess: procedure(res: array of Variant); onError: TSQLRestEvent;
      aReturnsCustomAnswer: boolean=false);
    procedure SyncCallRemoteServiceAsynch(aCaller: TServiceClientAbstract;      <---added
      const aMethodName: string; aExpectedOutputParamsCount: integer;
      const aInputParams: array of variant;
      onSuccess: procedure(res: array of Variant); onError: TSQLRestEvent;
      aReturnsCustomAnswer: boolean=false);
  ...
end;


implementation
...

procedure TSQLRestClientURI.SyncCallRemoteServiceASynch(aCaller: TServiceClientAbstract;
  const aMethodName: string; aExpectedOutputParamsCount: integer;
  const aInputParams: array of variant;
  onSuccess: procedure(res: array of Variant); onError: TSQLRestEvent;
  aReturnsCustomAnswer: boolean);
var  CallQueueItem : TCallQueue;
begin
    CallQueueItem := TCallQueue.create;
    CallQueueItem .caller := aCaller;
    CallQueueItem .callMethod := aMethodName;
    CallQueueItem .callParams := JSON.Stringify(variant(aInputParams));
    CallQueueItem .onError := onError;
    CallQueueItem .OnSuccess := onSuccess;
    CallQueueItem .ReturnsCustomAnswer := aReturnsCustomAnswer;
    CallQueueItem .ExpectedOutputParamsCount := aExpectedOutputParamsCount;

    fCallsQueue.add(CallQueueItem );                                // the length  is set in constructor Create (fCallsQueue.SetLength(0); )
    SyncCallRemoteServiceASynchQueue;
end;
 
procedure  TSQLRestClientURI.SyncCallRemoteServiceASynchQueue;
var Call: TSQLRestURIParams;
    CallQueueItem : TCallQueue;
begin
  SetAsynch(Call,
    lambda
      var i:= 0;
      fCallIsProcessed:= false;
      CallQueueItem :=  fCallsQueue[ Low(fCallsQueue)];
      for i := Low(fCallsQueue) + 1 to High(fCallsQueue) do
        fCallsQueue[i-1] := fCallsQueue[i];
      fCallsQueue.SetLength(high(fCallsQueue));
      if high(fCallsQueue)>=Low(fCallsQueue) then
         SyncCallRemoteServiceASynchQueue;
      if not assigned(CallQueueItem.onSuccess) then
        exit; // no result to handle
      if CallQueueItem.ReturnsCustomAnswer then begin
        if Call.OutStatus=HTTP_SUCCESS then begin
          var result: TVariantDynArray;
          result.Add(Call.OutBody);
          CallQueueItem.onSuccess(result);
        end else
          if Assigned(CallQueueItem.onError) then
            CallQueueItem.onError(self);
        exit;
      end;
      var outID: integer;
      var result := CallGetResult(Call,outID); // from {result:...,id:...}
      if VarIsValidRef(result) then begin
         if (CallQueueItem.Caller.fInstanceImplementation=sicClientDriven) and (outID<>0) then
           (CallQueueItem.Caller as TServiceClientAbstractClientDriven).fClientID := IntToStr(outID);
        if CallQueueItem.ExpectedOutputParamsCount=0 then
          CallQueueItem.onSuccess([]) else begin
          var res := TJSONVariantData.CreateFrom(result);
          if (res.Kind=jvArray) and (res.Count=CallQueueItem.ExpectedOutputParamsCount) then
            CallQueueItem.onSuccess(res.Values) else
            if Assigned(CallQueueItem.onError) then
              CallQueueItem.onError(self);
        end;
      end else
        if Assigned(CallQueueItem.onError) then
          CallQueueItem.onError(self);
    end,
    lambda
      if Assigned(CallQueueItem.onError) then
          CallQueueItem.onError(self);
    end,
    lambda
      result := (Call.OutStatus=HTTP_SUCCESS) and (Call.OutBody<>'');
    end);
    CallQueueItem:= fCallsQueue[Low(fCallsQueue)];
    if assigned(CallQueueItem) and not fCallIsProcessed then
    begin
       fCallIsProcessed:= true;
       CallRemoteServiceInternal(Call,CallQueueItem.Caller,CallQueueItem.CallMethod,CallQueueItem.CallParams);
    end;
end;

Eva
P.S. Thanks a lot for creating the SMS crossplatform  and I also would like to thank for warleyalex for illustrative examples and for creating videotutorials

Offline

Board footer

Powered by FluxBB