August 14, 2012 9:12 pm
In this third part of my series on BackgroundWorkers in Lawson Smart Office Scripts, I illustrate how to handle worker cancellation. This article is a continuation of my previous articles, Part 1 and Part 2.
When a worker is processing a time-consuming operation in a background thread, the user interface remains available and the user could potentially take any of the available actions, for example close the window (F3), refresh the M3 program (F5), click Previous (F12), click Next (ENTER), select an Option (CTRL+1-5), select a Related Option (CTRL+6-99), and go to Settings (F13). In those cases, I want to cancel my worker to stop the processing in a controlled way and avoid abrupt termination.
Fortunately, the life-cycle of Smart Office scripts includes three events I can listen to: Requesting, Requested, and RequestCompleted. I’ll intercept the user actions in those events and I’ll cancel the worker there. For more information on those events, refer to the Lawson Smart Office Developer’s Guide.
To cancel a worker, I use WorkerSupportsCancellation, CancellationPending, e.Cancel, IsBusy, WorkerSupportsCancellation, CancelAsync, and Cancelled.
First, I listen for the user actions:
controller.add_Requesting(OnRequesting); controller.add_Requested(OnRequested);
Then, I ensure the worker supports cancellation:
worker.WorkerSupportsCancellation = true;
In the background thread, I do the time-consuming operation as long as there’s no cancellation pending, otherwise I cancel the worker and stop the time-consuming operation:
function OnDoWork(sender: Object, e: DoWorkEventArgs) { var worker: BackgroundWorker = sender; while (someCondition) { doSomethingTimeConsuming(); if (worker.CancellationPending) { e.Cancel = true; return; } } ... }
In the UI thread, if the user takes an action I’ll intercept it in OnRequesting and I will instruct the worker to cancel processing:
// F3-Close, F5-Refresh, F12-Cancel, Option 5-Display, Previous/Next, ENTER, etc. function OnRequesting(sender: Object, e: CancelRequestEventArgs) { if (worker.IsBusy && worker.WorkerSupportsCancellation) { worker.CancelAsync(); } }
Then, the life-cycle of the script will reach OnRequested where I clean-up my script:
function OnRequested(sender: Object, e: RequestEventArgs) { sender.remove_Requesting(OnRequesting); sender.remove_Requested(OnRequested); }
Eventually, the worker will receive the instruction to cancel processing, and in OnRunWorkerCompleted I will do what’s necessary to stop processing in a controlled way and avoid abrupt termination.
function OnRunWorkerCompleted(sender: Object, e: RunWorkerCompletedEventArgs) { if (e.Cancelled) { // cancel } else { // continue } ... }
Here’s the full source code tested in Smart Office 10.0.4.0.38:
import System.ComponentModel; import Mango.UI; import MForms; package MForms.JScript { class Test { var debug: Object; var worker: BackgroundWorker; public function Init(element: Object, args: Object, controller: Object, debug: Object) { this.debug = debug; debug.WriteLine('start'); controller.add_Requesting(OnRequesting); controller.add_Requested(OnRequested); worker = new BackgroundWorker(); worker.WorkerReportsProgress = true; worker.WorkerSupportsCancellation = true; worker.add_DoWork(OnDoWork); worker.add_ProgressChanged(OnProgressChanged); worker.add_RunWorkerCompleted(OnRunWorkerCompleted); worker.RunWorkerAsync(); } function OnDoWork(sender: Object, e: DoWorkEventArgs) { var worker: BackgroundWorker = sender; var n = 10; for (var i = 0; i < n; i++) { worker.ReportProgress(i/n*100, 'processing'); doSomethingTimeConsuming(); if (worker.CancellationPending) { e.Cancel = true; return; } } } function OnProgressChanged(sender: Object, e: ProgressChangedEventArgs) { debug.WriteLine(e.ProgressPercentage + '% ' + e.UserState); } // F3-Close, F5-Refresh, F12-Cancel, Option 5-Display, Previous/Next, ENTER, etc. function OnRequesting(sender: Object, e: CancelRequestEventArgs) { debug.WriteLine('OnRequesting'); if (worker.IsBusy && worker.WorkerSupportsCancellation) { debug.WriteLine('cancelling'); worker.CancelAsync(); } } function OnRequested(sender: Object, e: RequestEventArgs) { debug.WriteLine('OnRequested'); sender.remove_Requesting(OnRequesting); sender.remove_Requested(OnRequested); } function OnRunWorkerCompleted(sender: Object, e: RunWorkerCompletedEventArgs) { if (e.Cancelled) { // cancel debug.WriteLine('cancelled'); } else { // continue debug.WriteLine('done'); } // cleanup var worker: BackgroundWorker = sender; worker.remove_DoWork(OnDoWork); worker.remove_RunWorkerCompleted(OnRunWorkerCompleted); debug.WriteLine('cleaned-up'); } function doSomethingTimeConsuming() { System.Threading.Thread.Sleep(1000); } } }
A normal execution will result in this output:
start 0% processing 10% processing 20% processing 30% processing 40% processing 50% processing 60% processing 70% processing 80% processing 90% processing done cleaned-up
A cancelled execution will result in this output, for example if the user presses F5-Refresh:
start 0% processing 10% processing 20% processing 30% processing OnRequesting cancelling OnRequested cancelled cleaned-up
Here’s a screenshot of both results:
In this article I illustrated how to intercept actions the user might take in the user interface while a time-consuming operation is in progress in a background thread, and eventually cancel the worker to stop the processing in a controlled way and avoid abrupt termination.
In my next article I will illustrate how to handle worker errors and how to handle exceptions.
All articles in this series:
Posted by thibaudatwork
Categories: Infor Smart Office Scripts
Tags: Script, Smart Office
Mobile Site | Full Site
Get a free blog at WordPress.com Theme: WordPress Mobile Edition by Alex King.
[…] Pingback: BackgroundWorkers in Smart Office Scripts – Part 3 « M3 ideas […]
LikeLike
By BackgroundWorkers in Smart Office Scripts – Part 1 « M3 ideas on August 14, 2012 at 9:15 pm
[…] ← Previous post Next post → […]
LikeLike
By BackgroundWorkers in Smart Office Scripts – Part 2 « M3 ideas on August 14, 2012 at 9:16 pm
..and beyond!!. Thanks dude!
LikeLike
By Juan V. on August 17, 2012 at 4:54 am
Hi Thibaud, deployed my php program to call a webservice by getting “Message: Function (“LstByNumber”) is not a valid method for this service”
It is in the wsdl, but cached one that doesnt have this method?… How do I clear?
Cheers Murray
LikeLike
By murray on September 5, 2012 at 9:34 pm
Hi Murray, it’s hard to troubleshoot your case with only little information. I suggest you first test your web service with SoapUI to validate it’s working; that will give you a reference SOAP Request. Then run your PHP code that calls the same web service. Intercept that call with Fiddler or Wireshark; that will give you a second SOAP Request which you say is failing. Compare both SOAP Requests and check for differences. There must be a significant difference that causes the error. Hope it helps.
LikeLike
By thibaudatwork on September 8, 2012 at 1:15 am
[…] I’m back from Burning Man and ready to write this fourth part of my series on BackgroundWorkers in Lawson Smart Office Scripts to illustrate how to handle worker errors and how to handle exceptions. This article is a continuation of my previous articles, Part 1, Part 2, and Part 3. […]
LikeLike
By BackgroundWorkers in Smart Office Scripts – Part 4 « M3 ideas on September 11, 2012 at 7:36 pm