Development Support

Home Forums kbmMW Development Support

Viewing 1 reply thread
  • Author
    Posts
    • #56013
      Ercüment USTA
      Participant

      Hi,
      While the server starts, I need to read global objects by SQL from database or from XML files in file system, then save them after passing them through some operations. And in some cases this information should be changed dynamically, in other words this information needs to be refreshed before the server is restarted.

      The renewal request is made by TkbmmwCustomService from any thread. When this happens, what I wish to do are as follows;
      1) Queue and hold the new incoming requests
      2) Existing threads to finish their jobs
      3) Objects to refresh
      4) System returning to normal.

      For this, I use the variable “boolean bxGlobals->NotifyServerProcessing:Boolean” with a method like below but I have doubts whether it is correct or not.

      Variant __fastcall TkbmmwMyCustomService::ProcessRequest(const System::UnicodeString Func, Kbmmwsecurity::TkbmMWClientIdentity* const ClientIdent, System::Variant const *Args, const int Args_High)
      {
      ..
      ..
      //Client tarafından gelen istek
      else if (AFunc == “NOTIFY”) return(NotifyServer (ClientIdent,Args,Args_High));
      ..
      }

      Variant __fastcall TkbmmwMyCustomService::NotifyServer(const Kbmmwsecurity::TkbmMWClientIdentity* ClientIdent, const Variant * Args, const int Args_High)
      {
      String NotifyString = Args[_argNotifyString];
      int TotalRequest = 0;

      if (NotifyString.Pos(“BYZCOLUMNTABLECHANGED”) == 0 &&
      NotifyString.Pos(“BYZTABLETABLECHANGED”) == 0 &&
      NotifyString.Pos(“SYSTEMINI…


      )
      return “”;
      //*******************************************
      while(bxGlobals->NotifyServerProcessing) ///??????
      Application->ProcessMessages();
      //*******************************************

      if (NotifyString.Pos(“ALREADYPREPARED”) == 0) {
      if (NotifyString.Pos(“BYZCOLUMNTABLECHANGED”) || NotifyString.Pos(“BYZTABLETABLECHANGED”)) {
      TbxSession *bxSession = new TbxSession();
      try {
      if (NotifyString.Pos(“BYZCOLUMNTABLECHANGED”))
      bxSession->GetByzColumnFromSession( );
      if (NotifyString.Pos(“BYZTABLETABLECHANGED”))
      bxSession->GetByzTableFromSession( );
      if (NotifyString.Pos(“BYZMENUTABLECHANGED”))
      bxSession->GetbxMenuTree( NULL );
      } __finally {
      delete bxSession;
      }
      }
      }

      //*******************************************
      bxGlobals->NotifyServerProcessing = true; ///?????
      //*******************************************
      Cardinal NotifyTickStart = GetTickCount();
      Cardinal TickBetween;
      try {
      try {

      //————————————–
      // Burada daha önceden işleme başlamış mevcut isteklerin bitmesini 60 saniyeye kadar bekliyoruz.
      // Bu işlem sağlıklı değil ancak farklı bir yöntem bulamadık.
      do {
      TotalRequest = 0;
      TList *ClientList = (TList *)bxGlobals->bxClientList;
      TbxClientInfo *bxClientInfo;

      for (int i = 0; i < ClientList->Count; i++) {
      if ((bxClientInfo = (TbxClientInfo *)ClientList->Items[i]) == NULL) continue;
      if ( bxClientInfo->ForceLogout) continue;
      TotalRequest += bxClientInfo->ActiveRequest;
      if (TotalRequest > 1) break;
      }

      Application->ProcessMessages(); // ????
      if (TimeBetween(NotifyTickStart, GetTickCount()) > 60000) break;
      } while (TotalRequest > 1);
      //————————————–

      NotifyTickStart = GetTickCount();

      if (NotifyString.Pos(“BYZCOLUMNTABLECHANGED”) || NotifyString.Pos(“BYZTABLETABLECHANGED”) || NotifyString.Pos(“BYZMENUTABLECHANGED”)) {
      TbxSession *bxSession = new TbxSession();
      try {
      if (NotifyString.Pos(“BYZCOLUMNTABLECHANGED”))
      bxSession->GetByzColumnFromSession(_SSColumn);

      if (NotifyString.Pos(“BYZTABLETABLECHANGED”))
      bxSession->GetByzTableFromSession();


      } __finally {
      delete bxSession;
      }
      }
      if (NotifyString.Pos(“JAVASCRIPTOBJECTSCHANGED”)) bxGlobals->ReloadObjects(_JavaScript);


      if (NotifyString.Pos(“SYSTEMINICHANGED”)) bxGlobals->ReadIniRuntimeChanges();
      } catch (…) {

      }
      } __finally {
      TickBetween = TimeBetween(NotifyTickStart, GetTickCount());
      try {
      if (TotalRequest == 1) // LastFunc == “NOTIFY”
      ErrorLn(__FILE__, __LINE__, _ErrError, “NotifyFinish. Process takes ” + FormatFloat(“#,###0.000”, (double)TickBetween / 1000.0) + “s.”, NULL);
      else ErrorLn(__FILE__, __LINE__, _ErrError, “Notifying can not Complete! (TotalReq:” + IntToStr(TotalRequest) + “)” + ” Process takes ” + FormatFloat(“#,###0.000”, (double)TickBetween / 1000.0) + “s.”, NULL);
      } catch (…) {

      }

      //*******************************************
      bxGlobals->NotifyServerProcessing = false;
      //*******************************************
      }

      return (String)((TotalRequest == 1) ? (String)”” : bxGlobals->ServiceErrorTable(_NotifyServerTryOut));
      }
      //—————————————————————————

      And I used the below code in Main.cpp, for holding the incoming requests at that time:

      void __fastcall TMainForm::TkbmMWMyTCPIPIndyServerTransport(TObject *Sender, IkbmMWCustomTransportInfo *Info,
      const IkbmMWCustomTransportStream *Stream)
      {
      #pragma warn -eff
      while(bxGlobals->NotifyServerProcessing)
      Application->ProcessMessages(); //// ????
      #pragma warn +eff
      }

      The examples I have given are written in C++, and the 64bit kbmmwServer component doesn’t work with C++, therefore we are converting the codes to Delphi for a new server application. Our RAD version is 11.0.

      I will appreciate your valuable comments and information on this. Thank you in advance.
      Best Regards
      Ercüment USTA

    • #56041
      kimbomadsen
      Keymaster

      Hi,

      It is a long piece of code you have copied in…. but I will try to comment.

      First it is an absolute no no to use Application->ProcessMessages within a service.
      In fact you should never use it at all, unless you have a _very specific_ reason that cant be solved in other ways. That usually do not happen.

      It seems to me that the scenario you are describing, is best handled by using queues.
      Create a kbmMW message queue (could be file based if you want it to survive across restarts, or could be memory based if you want to rebuild it on each start of the server).

      Create kbmMW messages and put those into the queue. The messages can be formatted with arguments etc.

      Eg.

      procedure TForm1.PushMessage(const AXML:string);
      var
         im:IkbmMWStandardMessageTransportStream;
         info:IkbmMWCustomTransportInfo;
      begin
           info:=TkbmMWCustomTransportInfo.Create;
           im:=TkbmMWStandardMessageTransportStream.Create(nil,info);
           im.Data:=AXML;
           YourQueue.PushMessage(im); // YourQueue is a TkbmMWMemoryMessageQueue or a TkbmMWFileStoreMessageQueue
      end;
      

      Then when you need to check if there are xml documents to be processed do:

      var
        sXML:string;
        im:IkbmMWStandardMessageTransportStream;
      begin
        im:=IkbmMWStandardMessageTransportStream(YourQueue).TentativeMessagePop;
        if im<>nil then
        begin
            try
              sXML:=im.Data;
              .... do something
            finally
              IkbmMWStandardMessageTransportStream(YourQueue).CommitMessagePop(im);
            end;
        end;
      end;
      

      Message queues are threadsafe and messages can be pushed from any thread and popped from any thread.
      Since the popping of a message is transactional (in to steps), you can pop a message off the queue (like the example), process it, and only when it has been processed successfully commit the pop, so the message for real is removed from the queue. If your processing fails, you can rollback the message pop instead, and have another go shortly. The message queue can be configured to automatically detect stalls (a message that is never processed of some reason) and do something specific with that, like auto delete it or push it to a different reject queue.

      Thus when you have some new XML to process you simply push it to the queue, and when someone wants the XML you pop it.
      Kim/C4D

Viewing 1 reply thread
  • You must be logged in to reply to this topic.