Tuesday, August 20, 2019

SysOperation framework basic Sync vs Async with or without custom controller

As a first sample we just want to run a method, no parameters

 

Step1, can create start by creating a service class


This is the class where we have the main processing logic is.

The method performs a summation from 1 to 10, but does not really matter what it does, your method will do something much more meaningful. What I just want to simulate is a long process of 5 seconds.

class FCH_SysOpSample2_OpService

{

}

[SysEntryPointAttribute(true)]

private void runSummation()

{

    int i;

    int val;

    for (i = 1 ; i < 10; i++)

    {

        val = val + i;

    }

 

    sleep(5000);//I want simulate a long process...

 

    info(int2str(val));

}

 

Notice the attribute SysEntryPoint which will explicit to the framework that this is the method to execute.

 

Step2, create a menu item Action

 

clip_image002

Notice that

  • Object we set SysOperationServiceController (which is the Operation framework base class for the controller, more in step 3)
  • Parameters we set our service class name and our method separated by a dot.
  • EnumTypeParameters we set SysOperationExecutionMode which is the enum of the operation framework which specifies which execution mode we want to use.

[as a side note: if you are curious how it works you can look in the main method \Classes\SysOperationServiceController\main which is called by the menu item. You will see that it calls this method \Classes\SysOperationServiceController\initializeFromArgs which parses the parameters from the args class]

Opening this menu item will show this dialog

clip_image003

If I click ok it will begin processing, and I can notice that the UI is blocked until it completes processing (we set 5 seconds…) and display the dialog.

If I would select Batch processing the process would be executed as a batch.

 

Step 3, create a controller

 

Now we expand our example by creating a controller so instead of calling from the menu item action the basic class of the controller we extend that same class so that we can control how it should behave.

In this first example we do not need to pass any parameter so we want to hide the dialog (by default a batch always shows a dialog where you can set the parameters and the batch execution).

class FCH_SysOpSample2_Controller extends SysOperationServiceController

{

}

public ClassDescription caption()

{

    return "enrico FCH_SysOpSample2_Controller";//is important to recognize the process in the batch history form

}

void new()

{

    super();

    this.parmClassName(classStr(FCH_SysOpSample2_OpService));

    this.parmMethodName(methodStr(FCH_SysOpSample2_OpService, runSummation));

    this.parmDialogCaption('Basic SysOperation Sample');

    this.parmExecutionMode(SysOperationExecutionMode::ReliableAsynchronous);//I could omit this parm since it is the default

}

public static void main(Args _args)

{

    FCH_SysOpSample2_Controller controller;

    controller = new FCH_SysOpSample2_Controller();

    controller.parmLoadFromSysLastValue(false);

    controller.parmShowDialog(false);

    controller.startOperation();

}

 

What I did here I had to override the new method where I set the service class and method name (what I set in the menu action parameter before) and the execution mode.

I added also a main method to run the class and here I set also that I do not want to see the dialog (in order for hiding the dialog the I also have to set parmLoadFromSysLastValue as false)

I could have also used the run() method instead of startOperation() but is not recommended.

If I run this class I will see that the UI is not blocked. When it finishes it will show the info box with the result.

It does not create a new batch, but it is still available in the batch history

System administration/inquiries/batch job jistory

 

clip_image005

 

One more note. Sometimes as noted in this post http://www.axdeveloperconnection.it/webapp/blog/reliableasynchronous-not-so-reliable-heres-why

The reliable asynchronous is not so reliable… works fine no worries, but the info log might not show up. To know more read that post. Here I just add the code you have to add to the controller class: override the afterOperation method like this:

protected void afterOperation(SysOperationExecutionMode _executionMode, AifAsyncResult _asyncResult)

{

    Batch batch;

    int pollCount;

    BatchHistory batchHistory;

    #SysOperation

 

    if (_executionMode != SysOperationExecutionMode::Synchronous)

    {

        progressWait.endWaiting();

    }

 

    if (_asyncResult.parmHasException())

    {

        error(strFmt("@SYS317009", this.caption(), _asyncResult.parmExceptionType()));

        return;

    }

 

    if (_executionMode == SysOperationExecutionMode::ReliableAsynchronous)

    {

        batch = this.operationReturnValue();

        if (batch)

        {

            if (batch.Info == conNull())//if infolog is not availabe

            {

                while (batch.Info == conNull())//while is not available

                {

                    pollCount++;

                    if (pollCount >= #BatchTableMaxPollCount)

                        return; //we have reached the max, so exit

                    sleep(#BatchTablePollingInterval);

                    select batchHistory where batchHistory.BatchId == batch.RecId;

                    batch.Info = BatchHistory::showLog(batchHistory.RecId);

                }

                infolog.view(batch.Info);

            }

            else

            {

                infolog.view(Batch::showLog(batch.RecId));

            }

        }

    }

}

 

here the full code for the controller class:

class FCH_SysOpSample2_Controller extends SysOperationServiceController
{
}
void new()
{
    super();
    this.parmClassName(classStr(FCH_SysOpSample2_OpService));
    this.parmMethodName(methodStr(FCH_SysOpSample2_OpService, runSummation));
    this.parmDialogCaption('Basic SysOperation Sample');
    this.parmExecutionMode(SysOperationExecutionMode::ReliableAsynchronous);//I could omit this parm since it is the default
}
public ClassDescription caption()
{
    return "enrico FCH_SysOpSample2_Controller";//is important to recognize the process in the batch history form
}
public static void main(Args _args)
{
    FCH_SysOpSample2_Controller controller;
    controller = new FCH_SysOpSample2_Controller();
    controller.parmLoadFromSysLastValue(false);
    controller.parmShowDialog(false);
    controller.startOperation();
}
protected void afterOperation(SysOperationExecutionMode _executionMode, AifAsyncResult _asyncResult)
{
    Batch batch;
    int pollCount;
    BatchHistory batchHistory;
    #SysOperation

    if (_executionMode != SysOperationExecutionMode::Synchronous)
    {
        progressWait.endWaiting();
    }

    if (_asyncResult.parmHasException())
    {
        error(strFmt("@SYS317009", this.caption(), _asyncResult.parmExceptionType()));
        return;
    }

    if (_executionMode == SysOperationExecutionMode::ReliableAsynchronous)
    {
        batch = this.operationReturnValue();
        if (batch)
        {
            if (batch.Info == conNull())//if infolog is not availabe
            {
                while (batch.Info == conNull())//while is not available
                {
                    pollCount++;
                    if (pollCount >= #BatchTableMaxPollCount)
                        return; //we have reached the max, so exit
                    sleep(#BatchTablePollingInterval);
                    select batchHistory where batchHistory.BatchId == batch.RecId;
                    batch.Info = BatchHistory::showLog(batchHistory.RecId);
                }
                infolog.view(batch.Info);
            }
            else
            {
                infolog.view(Batch::showLog(batch.RecId));
            }
        }
    }
}


 

 

No comments:

Post a Comment