Structured Error Handling in triggers confusion

Posted by James Palmer on 08-Dec-2015 04:51

In a piece of w code, if using BLOCK-LEVEL error handling, do I need to handle the error inside each trigger or can I somehow throw it back up to be handled in a single place in the code? I'll try and explain with a code snippet. 

ON CHOOSE OF BUTTON-1 IN FRAME fra-LinkMTS /* Button 1 */
  DO:
FIND metersite NO-LOCK. 
    CATCH PLE AS Progress.Lang.Error :
      MESSAGE 123
      VIEW-AS ALERT-BOX.
    		UNDO, THROW PLE.
    END CATCH.
  END.

MAIN-BLOCK:
DO ON ERROR   UNDO MAIN-BLOCK, LEAVE MAIN-BLOCK
  ON END-KEY UNDO MAIN-BLOCK, LEAVE MAIN-BLOCK:
  RUN enable_UI.
  RUN ColourSet IN gv-library-handle (INPUT FRAME {&FRAME-NAME}:HANDLE,
    INPUT-OUTPUT lv-current-window-handle).
  RUN Initialise IN THIS-PROCEDURE.
  IF NOT THIS-PROCEDURE:PERSISTENT THEN
    WAIT-FOR CLOSE OF THIS-PROCEDURE.

  CATCH PLE AS Progress.Lang.Error :
    UNDO, THROW PLE. 
  END CATCH.
END.


CATCH PLE AS Progress.Lang.Error :
  MESSAGE "An error occurred." SKIP PLE:GetMessage(1) SKIP PLE:GetMessage(2)
    VIEW-AS ALERT-BOX ERROR.
END CATCH.

So in the above, errors in Initialise are handled by my custom message, but whilst the catch in the trigger is firing, I still get the standard Progress error message, and not the custom one I've defined. If I put my error handling code in the trigger's catch then it works as I'd hope, but I'd rather just handle the error messaging in one place.
Obviously I can move my error handling to a class and have a central way of dealing with it there, but I'm trying to understand what's happening.

All Replies

Posted by Mike Fechner on 08-Dec-2015 04:57

It's best practice to have a default CATCH block at the end of each UI event handler (trigger).

Ours looks like this here. ErrorHelper:ShowErrorMessage show as the error  in a nice dialog, with all relevant session info (from client and AppServer) and in 11.6 all properties that a custom error class may have (using reflection).

If you don't show the error nicely, the AVM does it in the good, old, ugly error message box. Also be aware, that the Custom error dialog might also handle AppErrors with only a ReturnValue, but no message (UNDO, THROW NEW AppError ("This is a return value") vs. UNDO, THROW NEW AppError ("This is a message", 0).

CATCH err AS Progress.Lang.Error:
    Consultingwerk.Util.ErrorHelper:ShowErrorMessage (err).
END CATCH.

Posted by James Palmer on 08-Dec-2015 05:01

Thanks Mike. Yes I have drooled a lot over Marko's demo of your error handling at the PUG. It's certainly at a completely different level to anything I've ever seen before! :)

Posted by Mike Fechner on 08-Dec-2015 05:04

Not sure if that's good or bad :-)

Posted by James Palmer on 08-Dec-2015 05:09

Well, it's bad that I've not seen that level of handling before ;)

Posted by Laura Stern on 08-Dec-2015 08:37

Mike is correct, of course.  It is a common misconception that throwing an error out of a UI trigger can (or should) get caught by a CATCH in the block containing the WAIT-FOR statement.  But it will not. A CATCH block cannot run unless that block is where the current execution stack is (what would traditionally be called the "program counter").  The only way that a CATCH block associated with the WAIT-FOR block could run is if the WAIT-FOR is over.  And clearly that is not the desired effect.  Throwing an error out of a UI trigger is essentially throwing an error out to the Windows OS.  Since that makes no sense, the AVM traps it and handles it the "old fashioned" way, which is to just put up an error alert box.

What people seem to want is a global CATCH block (kind of like a persistent UI trigger) that really has no association with any coded block.  It just sort of sits in space and catches errors that are thrown out of UI triggers.  But there is no precedent for that in any common language that we know of and there are no plans to introduce such a concept.

Posted by James Palmer on 08-Dec-2015 08:42

Thanks Laura. Appreciate you taking the time to reply. I'm very wet behind the ears when it comes to anything along the lines of this so am using every opportunity to learn by trying, and hence the confusion!

Posted by Laura Stern on 08-Dec-2015 08:50

No problem.  As I said, this is a COMMON misconception.  It's not just you.  I'm happy to help clear the confusion.

Posted by Peter Judge on 08-Dec-2015 10:50

You can also remove the CATCH after the WAIT-FOR statement - it will never execute. The wait-for is a bit of a Gandalf statement.  If you want errors to span a wait-for, you have to resort to jiggery-pokery along the lines of

/* this variable is defined at the class level. */
define private variable moReadHandlerError as Error no-undo.


method public void WaitForResponse(input piTimeout as integer):
	define variable iStartTime as integer no-undo.
	
	Assert:IsZeroOrPositive(piTimeout, 'Timeout').
	assign moReadHandlerError = ?
		   moReadEventArgs = new SocketReadEventArgs(piTimeout)
		   iStartTime = mtime
		   mhSocket:sensitive = true.
	
	wait-for 'U2':u of mhSocket.
	LogMessage('READ: TOTAL TIME(ms)=':u + string(mtime - iStartTime), 5).
	
	/* 'catch' the errors from the handler after the wait-for. */
	if valid-object(moReadHandlerError) then
		return error moReadHandlerError.
end method.




My usage is a socket event handler and so there's no UI that I want to display any errors on. I didn't want to have random errors popping up in logs. In my Read-response-handler I have code that catches errors and executes the U2 event which then sends error on its way.

Posted by Marian Edu on 08-Dec-2015 11:39

not sure about 'common' but that works just fine in node.js... nodejs.org/.../process.html

not really ui but a catch all nonetheless, and as you say there is already something in place what need to be added is an option to add a handler to that instead of 'old fashioned' default handling :)

Posted by Laura Stern on 08-Dec-2015 12:16

.NET also has a way to catch an unhandled exception - by subscribing to:

   AppDomain.CurrentDomain.UnhandledException

I think this is analagous to this unwantedException in nodejs.  But at least in .NET, by the time such an event handler fires, the app has unwound and is about to terminate.  What people want is to trap the exception and just keep going as if it didn't happen (assuming the trigger code itself doesn't do something to shut the app down).

Posted by Marian Edu on 08-Dec-2015 12:36

in node.js that will kill the app as well, unless caught there and then nothing 'bad' happens and things keep on going... unless the handler choose to shut it down of course

Posted by Mike Fechner on 08-Dec-2015 12:39

[quote user="Marian Edu"]

in node.js that will kill the app as well, unless caught there and then nothing 'bad' happens and things keep on going... unless the handler choose to shut it down of course

[/quote]

I'm sure I will regret saying that ... but that sounds like a clever implementation.

Posted by Marian Edu on 08-Dec-2015 12:49

[quote user="Mike Fechner"]

I'm sure I will regret saying that ... but that sounds like a clever implementation.

[/quote]

hehehe, can't believe you still don't like javascript ;)

Posted by Mike Fechner on 08-Dec-2015 12:52

The more I am forced to use it the more I hat it.

Posted by Marian Edu on 08-Dec-2015 13:09

think it's just the gauss bell curve, keep calm and wait-for the peek to pass :D

Posted by Mike Fechner on 08-Dec-2015 13:12

Sure.... after a short high it's going DOWN steep. We have ABL and C# and TypeScript. Who need an unstructured language?

Posted by James Palmer on 09-Dec-2015 02:05

Thanks for all the help with this. I've actually implemented a similar solution to error message displays to Mike's and it took me about 3 hours to do. Not as hard as it first sounds!

Posted by ske on 09-Dec-2015 07:28

> Throwing an error out of a UI trigger is essentially throwing an error out to

> the Windows OS.  Since that makes no sense, the AVM traps it and handles it

> the "old fashioned" way, which is to just put up an error alert box.

So the AVM already traps these errors from UI triggers during WAIT-FOR and handles them by displaying a default alert box.

It would seem reasonable to have a way to just replace this standard error handling with user specified program code, when desired. Having a way to replace default handling of any kind is often useful.

Posted by slacroixak on 22-Jan-2016 07:32

I missed this discussion last month that is pretty close to the recent "How to catch this untrapable STOP" one.  I am now hitting this (documented) limitation while restructuring some classical UI .w procedures with structured error handling (BLOCK-LEVEL ON ERROR ... all along the way).

My main concern is to catch the CallStack Trace of an unexpected error that occurs somewhere deep in new OO ABL methods, so we can investigate it.  The only work around I can manage to achieve is a single line include file to replace the "END" statement of a UI trigger. (single line to not affect line numbers in the debugger)

/* END_UITriggerCatchError.i */  CATCH ple AS Progress.Lang.Error:  Tools:ShowException(ple).  END CATCH.  END.

Like others said in this discussion, I really wish we could overcome this limitation and educate the WAIT-FOR to let an error object be caught by a CATCH in its block.  By the way, I have no problem to catch ABL error objects when an error is thrown by an ABL method invoked by some .Net WPF controls UI triggers...

Now, if it not possible to educate the WAIT-FOR this way (I have to confess I fall in the category of people with the "common misconception" issue), then I do not see why the ABL could not be a first un-common language providing the ability to catch an error message and its call-stack (when -errorstack is eabled, of course) and pass it as a kind fo Progress.Lang.whateverError to some custom error handling code.

Why being a follower when we could enjoy being a leader? (at least is a little space...)

Is there any logged Enhancement Request for such a feature, or should I log it one day?

Posted by Laura Stern on 22-Jan-2016 08:12

You can wish all you like but catching an error in a CATCH block of the WAIT-FOR when the WAIT-FOR has not yet terminated will NEVER happen.  It simply is not correct and possibly not even possible.  However someone else suggested being able to specify a callback as an override for the default error handling behavior.  This sounds more feasible and is on our radar.

And I'd just like to say that doing error handling and dealing with it everywhere it needs to be dealt with is commonly just a pain in the butt, in every language I've ever used.  Nothing new for the ABL.  

Posted by Laura Stern on 22-Jan-2016 08:23

P.S. But we do understand the challenges of retrofitting old code to deal with a new error handling model.  It is much easier when you can build it in the first time.  So didn't mean to sound unsympathetic!  

Posted by slacroixak on 22-Jan-2016 09:23

Thank for your prompt jump at it Laura.  You may notice that in my post, I confess although I have little problems to understand the limitation, I can somehow accept it ("Now, if it not possible[...]")

I believe the default error handling override callback you have in your radar would probably make me happy.  If there is a public ER about it then I wish (reasonably) I could give my vote for it...  I just tried to find something about this topic in the Community pages without success... well, I mainly missed a search facility scoped to the so called 'Ideas' part.

At last, about your last note: you might already know the opportunities to develop something brand new are rather rare (no inexistant but rare indeed), and we often have to come back to some old Big App that produces the cash.  Any improvement to better keep these dark corners under control are welcome indeed.

Posted by Laura Stern on 22-Jan-2016 10:55

You're correct, I don't think there is a public ER for the callback idea.  You're welcome to log it .

Posted by OctavioOlguin on 23-Jan-2016 16:33

Any chance that we could get a view to the doc you mention on :

"Thanks Mike. Yes I have drooled a lot over Marko's demo of your error handling at the PUG. It's certainly at a completely different level to anything I've ever seen before! :) "

Posted by Mike Fechner on 24-Jan-2016 02:42
This thread is closed