PDA

View Full Version : When to use Try/Catch?


sonam
08-15-2006, 22:03
Hi - What are your views on when to use Try/Catch construct in Flow services? (The Try/Catch construct is defined in GEAR 6 page 17)

1. Some people have the viewpoint that _all_ user-created services should have their code wrapped by a 'Try/Catch' construct. (Even if all the 'Catch' block does is log an error and exit). For eg:

TRY
doThis
doThat
CATCH
getLastError
logError
EXIT with failure



2. Others say use Try/Catch only for "top-level" services -- i.e. a services invoked by external users, or "interface" service -- i.e. services which make a connection to another system (Even if all the 'Catch' block does is log the error and exit.) Example: same as above.

3. Others hold the view that Try/Catch should only be used in a service if the built-in IS exception handling is not sufficient. ie. The catch block needs to do something different. For example below, it rolls back a failed transaction:

TRY
doThis
doThat
CATCH
getLastError
logError
doRollback
EXIT with failure


Which of these 3 options - or other alternative - do you favor?

Regards,
Sonam

mcarlson
08-16-2006, 00:15
Sonam,

I'm in Camp 1. I use try-catch for all of my Flow services. Usually, I'm building them individually and having try-catch makes that process go more smoothly. Also, if you standardize your catch-block structure or place a standardized logging service call there, consistency of error handling is improved across the organization.

I'm sometimes asked to assess the quality of work performed by other consulting firms. When I find massive numbers of Flow services with nary a try-catch sequence in sight, that's a sign that I'm dealing with rank amateurs in most cases.

Mark

sonam
08-16-2006, 04:40
Thanks Mark. It's interesting you use Try/Catch in all services (including utility services?)

Where customized error logging is not used I stay in Camp 3 myself... ie I don't use 'Try/Catch' unless the 'Catch' block adds more value than the built-in exception handler.

Where customized error logging must be used, I am in Camp 2 -- a 'Try/Catch' block is used in 'top level' services (entry-points for transactions). Exceptions thrown downstream 'bubble-up' to the top-level and are logged with stacktrace. (Except for transformers IIRC).

A couple of disadvantages with Camp 1:

(1) The extra code makes errors possible -- For eg: wrongly setting the exit condition in a sequence, or even simple transposition errors like:
TRY
doThis
doThat
CATCH
getLastError
EXIT with failure
logService

(2) the Flow becomes a few clicks more difficult to read

From what I see of webMethods-built services, they are firmly in Camp 3.
Eg: wm.tn:receive (WmTN), sample.soap:buildRPC_sendHTTPSimple (WmSamples)

Regards,
Sonam

mcarlson
08-16-2006, 05:21
I would not necessarily consider built-in (or generated) Flow services to be the "gold standard". Just look at the web services connectors for one bad example, although one that uses try-catch. :)

I agree that it takes too many steps to configure a try-catch sequence structure. That's usually the first thing that I do when I create a Flow, but it should be as simple as clicking a button or writing a macro and then clicking a button. I know certain WM developers who were lobbyign for this almost 3 years ago, but I don't think we've had much in the way of Developer ease of use improvements since the 6.0 to 6.1 change.

Mark

srikanth05
08-16-2006, 08:41
i personally like to use try/catch in all of my services ..actually thats the first thing i do (as mark specified) when i start a flow ..
but recently the company ,which i work for has changed its try/catch standards ..they want to just have try/catch only in the parent service ..and any child flow service which it uses will just carry the error back to the main catch block.
but i guess creating each and every service with its own try/catch is the neat way of coding ..

sri

spg
08-16-2006, 09:08
If you have a parent and child flow service type of scenario, catching the exceptions in the child flow services also might improve the granularity of information you want to put in the log.
For example if you are parsing a file at different levels in the parent and the child flow services you can include more information in the log about the file/content which helps you debug.

Shubhro

reamon
08-16-2006, 10:07
Generally, I use approach 2, though I avoid use of try/catch whenever possible (use of exception-style programming is another debate). Just like Java/C++ code that doesn't have every method using try/catch blocks, I use try/catch more judiciously. Usually, it's the top-level services--if they need to catch errors, which is almost always.

I use try/catch in "lower" services only when it's necessary (commit/rollback, close, etc.). I've never worked on a project or in a company where all user-created services had try/catch. It's rare for one my utility services to use try/catch.

In a couple of frameworks I've worked with it was absolutely essential that lower-level services didn't catch errors (unless they needed to to commit/rollback, release connections, etc.--and then they needed to rethrow the error). This is because the framework managed the logging and notification processes. Effectively a "top-level try/catch" approach. Errors were also managed by the framework to help manage retries and isolate logging (logging scattered everywhere becomes painful).

I think the key here is to have an approach for try/catch use, and understand when the standard approach needs to be overridden/disregarded.

jlammers
08-16-2006, 18:06
Here's the rules that I usually follow:
Always use a Try/Catch sequence in a top-level service that is not invoked from a model.
When not in a top-level service, if you are not going to do something useful when you catch the exception, then don't use a try/catch at all. This is unneeded overhead.
If you do catch an exception and you are not at the top of the call stack, then either handle it gracefully or return a more meaningful message to the caller.
Always do a pub.flow:getLastError as the first statement in your catch block.
Remember that a try/catch sequence in webMethods has the same variable scoping rules as a try / catch in Java (but no compile time checking for scope issues). So, if you need access to a variable in the catch block, make sure that you have defined it before entering the try block. As soon as you enter the catch block, any variables that were defined within the try block will not be available (except via the lastError/pipeline structure - but I typically avoid trying to code against this structure since you don't really have access to it at design time).
I like to break out exceptions into three "classes":
Exceptions that you are expecting to occur and can gracefully handle.
Exceptions that you are expecting to occur, but cannot gracefully handle.
Exceptions that you are not expecting to occur.A bit more on each...
Exceptions that you are expecting to occur and can gracefully handle
In this case, you will take some sort of corrective action in your code (if needed) so that the exception is transparent to the client.

Exceptions that you are expecting to occur, but cannot gracefully handle
In this case, you cannot hide the fact that there is a problem. You will need to agree with the client on either passing back some sort of response code and message or throwing an exception of a particular type. Which method you choose to use will depend largely on the protocol that you are using and the capability / preference of the client. The client may choose to retry the transaction or take other actions on their own.

Exceptions that you are not expecting to occur
In this case, you can do nothing. This will be a fatal condition, however you still need to agree with the client on how the exception will get raised up. However, since the exception is of an unknown type (i.e. it was not preconceived that it might occur), there may be no safe corrective action that either you or the client can take.

sonam
08-16-2006, 23:55
Good discussion everyone.

Mark - thanks for the inputs... macro or code template functionality would be great. Actually if webMethods implemented some nifty ideas from Aspect Oriented Programming (For eg: "Do logging for all mycompany.print.* services with handler: ..."), there would be some cool benefits.

Rob - a concise, sensible, and perfectly justified policy as usual. Quoting you again (just because it's so good) :)
Generally, I use approach 2, though I avoid use of try/catch whenever possible (use of exception-style programming is another debate). Just like Java/C++ code that doesn't have every method using try/catch blocks, I use try/catch more judiciously. Usually, it's the top-level services--if they need to catch errors, which is almost always.

I use try/catch in "lower" services only when it's necessary (commit/rollback, close, etc.). I've never worked on a project or in a company where all user-created services had try/catch. It's rare for one my utility services to use try/catch.


Hi "jlammers" -- that's a good set of rules. Got a question about the 1st one: "Always use a Try/Catch sequence in a top-level service that is not invoked from a model." -- What's special about services invoked from a model? Does BPM implement built in error handling?

mcarlson
08-17-2006, 08:45
This is a good discussion and thanks, Sonam, for kicking it off! It's given me some food for thought and some ideas for improving my custom SOAP processor error handling.

Mark

jlammers
08-17-2006, 15:31
Hi "jlammers" -- that's a good set of rules. Got a question about the 1st one: "Always use a Try/Catch sequence in a top-level service that is not invoked from a model." -- What's special about services invoked from a model? Does BPM implement built in error handling?
Yes, there is error handling of sorts. You can designate a process-wide error step and/or you can define an error transition. What method you use really depends upon the situation. That being said, the important thing is that you probably want to mark the model as failed. I typically explicitly set this in the process wide error step or, if you don't have a process-wide error step and your service throws an exception, then the process will automatically get marked as failed.

The reasons that I generally don't put a try / catch in the services that I call from a model:
Usually I write a wrapper service called stepNameImpl, so there's not a whole lot going on that could produce an exception.
In a business process, if I'm able to handle some exception condition gracefully, then usually I've already done that in a lower level service. If there are any remaining exceptions bubbling up, I probably can't do anything useful with them, so I will have to mark the process as failed - in which case there is no reason for a try / catch.As with anything, these are not hard and fast rules, but that is my thought process behind not using a try/catch for services invoked by models.

I think it really all boils down to - "Can I do anything useful with the exception?"

One last thought - the coarseness (or granularity) of your logic within the process model will also dictate which approach to take. Very coarse process models will likely just use a process-wide error step - more fine-grained ones will probably use some combination of process-wide error step and error transitions.

--jeff lammers

rocky_rhapsody
08-18-2006, 14:58
May I add, 2 little cents...

I have also used nested try/catch, to monitor stuff. Example: Some applications in our enterprise are flaky. Our scheduler consistly checks the health of the database, by pinging it. If dead, the scheulers, filepollers, triggers....are turned off and call bunch of other services.

The top level services may NOT neccessarily have try/catch. Child may be them as well. The logic of "Sequence" SUCCESS, FAILURE and DONE, may became tricky. We do use broker to lose the thread in these cases and do extensive filtering at trigger level.

sonam
08-21-2006, 02:53
Thanks Jeff and everyone else in this discussion.

nath
08-30-2006, 05:09
I personally hate coming across every single service with try-catch when all it does is clutter things up. I treat flow try-catches just like java methods, sometimes it is appropriate for a method to need a try catch, other times it does not catch it and "throws" the exception back up to the parent. Some things are exceptional exceptions, others are expected exceptions.. Using too much try-catch tends to hide or incorrectly handle the exceptional exceptions.

I treat it like writing java code: so generally NOT with everything surrounded in try-catch structures, so same with flow code. I've come across a couple of attempts at coding standards or code quality checklists that I've had to un-drum the obsession with try-catch in every single service.

Top level services are always useful ones to have try-catch as a "last resort catch and log the error" type thing.

If the exception is caught too low down due to excessive try-catches then your service might not blow up like it should. Steps like map services look ugly with try-catches..
To roughly summarise:
* top level services
* services where the error handling has business logic or more in it than simply logging the error
* anything using explicit transaction management to allow for calling the rollback
* generally network operations and things which are quite likely to blow up
* services that return different values or set flags when an error within them occurs (e.g. you do an isDatabaseAlive service that returns a true false value)

regards,
Nathan Lee (http://jroller.com/page/nathan)

ybedu
09-05-2006, 16:02
Hello,
It is ofcourse common to see a set of sequences that assimalate the java like try catch. One thing that is not the same, is the scope in the initial excuting sequence. All values are rolled back to before the sequence was called and so even if you define the field before hand, it will only have its previous value (pipeline rollback).

I have not heard anyone specifically mention the Event Manger. Is it not suitable for separating the certain actions, like logging, that are asynchronous and auxillary to program flow? It almost sounded like sonam was partly speaking of this but maybe you were referring to something more powerful? I use it for logging exceptions through a class of services to a specific location. It saves about 4 lines of code per service.

I would like a retry (with preserve) construct and to have code-watches with triggers (wwt). The wwt is having an action triggered by a particular state in the FLOW being true. Pretty much it is emboding aspects of the publish and subscribe model we use to the FLOW code at the document AND field level with less kludge. You could have a separate pane that you assign a service to run when a certain condition is met.

I feel awry about nested sets of these. That was something that ate at me to do right one time. The region was small enough to not warrant a whole separate flow service, but its error handling was complex enough to not be solvable at the same level. Some values needed to be in place to preserve field mutations before a following step was executed. I did want to know some of the values and allow others to rollback. an example:

map a = 0

seq exit-on:success
- seq exit-on:failure

-- map b = 1

-- seq exit-on:success
--- seq exit-on:failure
---- map c = dynamicsql3 b

--- seq exit-on:failure
---- map d = dynamicsql4 b

--- seq exit-on:done
---- map b = -1 ## keep b; rollback c,d

-- map ## operate on b, c, d

- seq exit-on:done
-- map a = -1 ## keep a; rollback b

map ## operate on a, b

For the most part, I treat transient errors as hiccups, so I use instead a REPEAT step and let it run 3 times with a sane time between. If it really was simply a delayed connection or waiting file state, I would soon have proper access, otherwise I allow the service to error as if I was sure the resource would never be obtainable. That is a wrapper to the normal call and the parent will handle any needed exception situations. It could be a second wrapper with a single seq exit-on:done that did a boolean return. That in fact is how I setup service that I would only do a log in error and continue the rest of the execution. The logging would be dumped off to Event Manager.

If you are going to include the try-structure in a recursive service, try to limit the re-entry point to one location. I just generally also try to not have mutually recursive definitions as that is too magical for me in all but very simple cases. Good day.

Yemi Bedu

sonam
09-06-2006, 02:59
Hi Nath - good to see you back.

Hi Yemi -
It is ofcourse common to see a set of sequences that assimalate the java like try catch. One thing that is not the same, is the scope in the initial excuting sequence. All values are rolled back to before the sequence was called and so even if you define the field before hand, it will only have its previous value (pipeline rollback).

I have not heard anyone specifically mention the Event Manger. Is it not suitable for separating the certain actions, like logging, that are asynchronous and auxillary to program flow? It almost sounded like sonam was partly speaking of this but maybe you were referring to something more powerful? I use it for logging exceptions through a class of services to a specific location. It saves about 4 lines of code per service.


Pipeline rollback is a very important point that distinguishes sequences with 'exit-on: SUCCESS' from other sequences or from Java services -- it's good you raised it Yemi.

Thanks for also mentioning raising Event manager - it's one of those things I kept intending to investigate and use, but somehow never got around to doing. I was on a training course last week, and a person there described using Event manager just as you do -- to subscribe to exceptions and log them uniformly in a custom logging framework. It would be good knowing your experience and gotcha's using event manager for logging.

I know only a bit of Aspect Oriented Programming (AOP)...
(Good description here: http://en.wikipedia.org/wiki/Aspect-oriented_programming)
While AOP is probably more powerful, what webMethods have done in Event manager is somewhat similar: the Event manager subscription and filters define "join points" in AOP-lingo and the subscribing services are analogous to AOP "advice".

Regards,
sonam

ybedu
09-07-2006, 08:26
Hello,
I am speaking of only on 6.0.1 by the way, so improvements in the latest are not quite yet known to me.

It is very simple to play with and get the gist of where you can go with Event Manager. For example, i have a simple handler that will parse out the pipeline (expand lists and table to structured text strings) and recursively execute on the nestedException. Each recursive invoke will chain down the exceptions and bubble back up to a final table.

I can use this to assign a general portion to special handlers. One that I have, sends an email per exception unit. So if you fail on one service, you will get a bunch of emails on the calling stack of services to the top non handling caller. Another can save the lot as one file or as a group of files.

One of its downsides in my book is that only the service name is a choice for the filter. I would have liked to of been able to somehow specifiy an Exception type. I also would have liked to of been able to choose whether the execution is synchronous or async. This way a logger could be async and a database connection or file handle closer could be synchronous. Good day.

Yemi Bedu

gazi00us
09-13-2006, 05:06
Hi All,

In camp 1 & 3, sonam has listed that in Catch bloch "EXIT with failure", do we need to explicitly specify the exit step in catch block. I understand that
if the sequence of catch block is set to exit at done, that will do the purpose.
Is there any performance/resource implication of using explicit exit step in catch block, should we use or not..?
Thanks,
KK

ybedu
09-13-2006, 10:48
Hello,
The exit{signal failure} will not work in a seq that has "exit on done" or "exit on success" set. I didn't completely read sonam's examples before (had to scroll the mini pane/ ignoring the illogical). The way sonam's example would work is to have the typical:

seq: exit on success
- seq: exit: on-failure
- seq: exit: on-done

branch: switch:exception
- seq: exp1 exit: on-failure
-- exit: signal: failure ## AN APPROPRIATE POINT FOR EXIT
- seq: exp2
-- map
-- exit: signal: success

It is probably good to not read it literally as a line by line representation. There are other posts with more elaborate examples of setting up the try-catch-dispatch pattern. Good day.

Yemi Bedu

sonam
09-19-2006, 20:55
The exit{signal failure} will not work in a seq that has "exit on done" or "exit on success" set.

I'm puzzled about this statement -- here's the section from "GEAR 6 webMethods Developer’s Handbook" Pg 17 that seems to say that an EXIT with failure as the last step in a sequence set to exit-on-DONE is fine:

SEQUENCE1 (main sequence, set to exit on SUCCESS)
SEQUENCE2 (logic sequence, set to exit on FAILURE)
Service1
Service2

SEQUENCE3 (error sequence, set to exit on DONE or as required)
pub.flow:getLastError
EXIT ‘$flow’ and signal FAILURE


The purpose of the 'EXIT and signal failure' in my example would be to bubble the error up to the calling service. Would this not work?

Sonam

castropb
09-20-2006, 07:20
It works fine. I use it all the time.

NOTE: although EXIT and Signal Failure works fine from within the "catch block" (ie. SEQUENCE EXIT ON DONE), pub.flow:throwExceptionForRetry does NOT. This can be a little confusing to some.

jlammers
09-20-2006, 12:41
This should work fine. Check your SEQUENCE1 to make sure it is EXIT ON SUCCESS and not EXIT ON DONE. That could give you the behaviour you're seeing.

Other things you may want to look at:

Is the EXIT (SIGNAL FAILURE) statement in your catch block the last statement?

Is this service being called by another service? If so, is it being called from within the "catch block" of the other service?

Is the service invoke that originally produces the error being called from within a transformer? (With some SP / Fix levels in IS 6.1 errors generated from services called within a transformer do not branch to the catch block properly. Furthermore, if the service that has the "erroring transformer" is called from something like an SAP listener (i.e. native code), it will take down the IS).

If you are still not having luck, upload a sample of your code, your IS version / SP level etc.

--jeff

jlammers
09-20-2006, 13:09
This should work fine. Check your SEQUENCE1 to make sure it is EXIT ON SUCCESS and not EXIT ON DONE. That could give you the behaviour you're seeing.
Sorry - should have tested this first - this does not produce the behaviour you are seeing.

Is the EXIT (SIGNAL FAILURE) statement in your catch block the last statement?
This also has no impact.

The other two scenarios that I describe might explain it...

Is this service being called by another service? If so, is it being called from within the "catch block" of the other service?
If the catch block in the calling service does not have an exit signal failure, then this might explain it...

Is the service invoke that originally produces the error being called from within a transformer? (With some SP / Fix levels in IS 6.1 errors generated from services called within a transformer do not branch to the catch block properly. Furthermore, if the service that has the "erroring transformer" is called from something like an SAP listener (i.e. native code), it will take down the IS).
In this scenario, it will never even enter into the catch block...

Sorry for the erroneous previous post...

--jeff

ramchalluri
09-20-2006, 18:56
Going back to the discussion on being in CAMP 1 or 2. My question is, if we catch an exception in child services and rethrow them what happens to the pipeline.

e.g.

parent service

try
A = 123;
B = ABC;
C = XYZ;
CALL CHILD SERVICE1 (A, B)
catch



CHILD SERVICE 1(A, B)

try
A = 321;
B = CBA;
1/ 0
catch
getLastError
log the error
throw with failure message;


So what happens to the pipeline. Will the pipeline variables A,B is modified to 321 and CBA in the parent service or will it stay as it was in the parent service before calling the child service which is 123 and ABC.

Ram Challuri

jlammers
09-20-2006, 20:57
Neither...

In the sample that you gave A and B (and C for that matter) will have no values after the exception bubbles back up. Strings are not merged back into the parent pipeline until the successful execution of a code block (i.e. sequence, flow service etc.). Since A and B were defined in the "try" in the parent service, they will not have any value when you jump into the "catch". Had you defined and initialized A and B before the "try" in the parent service, then they would have the values 123 and ABC respectively after the exception bubbled up (assuming you do not "exit signal failure" in your parent catch block).

BTW - why not just try this example for yourself to see what happens?

ramchalluri
09-21-2006, 18:33
Sorry, but I have tested it my self and I know the answer for the above but my question was what should be the correct behaviour and what impact will that bebaviour have on using the try / catch block in the child service.

My finding are as follows.

If we use try / catch in the child service then

The parent pipleline values are not changed at the same time the lastError.pipeline.variable values are not changed either. where as if we don't use the try / catch in the child serivce the parent pipleline remains the same but the lastError.pipeline.variable values will be latest.

More then often we would want to know exactly what was the last thing happened before the exception.

Ram Challuri

ybedu
09-27-2006, 10:14
Hello,
Yes you are correct sonam. The way you have it explained in your response to my false statement was correct. The exit (at any point) forces an exit from an immediate sequence step (parent), loop/repeat (loop), or containing flow (flow). I am sorry to make such a mistake that would have people possibly doubt your positive, correct (structurally), and well thought suggestions.

The way which I showed is just one of many possibilities thanks to FLOW's flexible SEQUENCE constructs and our creative ways. Good day.

Yemi Bedu

sonam
10-08-2006, 21:51
Yes you are correct sonam.

Thanks Yemi! Appreciate your clearing that up, bro. :)