<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Gents,<br>
<br>
Our current exception use/handling in conductor is less than ideal.
I've tried to highlight some of the issues here, and proposed an
approach that I feel we should take when it comes to exceptions
handling. Anyone with thoughts on this topic please chime in.<br>
<br>
Cheers<br>
<br>
Martyn<br>
<br>
--<br>
<br>
So, we use the below syntax in many places in conductor code. Using
this syntax to raise an exception is problematic:<br>
<br>
raise "Some error occurred while trying to do something"<br>
<br>
Why?<br>
<br>
There is no way for conductor to interpret the cause of this
exception, and react accordingly.<br>
<br>
Example1: Recovery of errors<br>
<br>
(of course this could be rewritten in a way to not use custom
exception but is just a simple example to demonstrate the need)<br>
<br>
<blockquote>def perform_task<br>
case @task.name<br>
when "Start Instance": start_instance(@task.realms[0])<br>
...<br>
end <br>
end<br>
<br>
def start_instance(realm)<br>
...<br>
# Instance failed realms full<br>
raise "Could not start instance realms are full"<br>
end<br>
end<br>
</blockquote>
<br>
In the above code, perform_task calls start_instance which raises an
exception with the user message "the realm is full". This then
gets filtered up the chain and caught in the ApplicationController
which logs the error and filters the message back to the user.<br>
<br>
-- Using Custom Exceptions<br>
<blockquote>def perform_task(task)<br>
begin<br>
case task.name<br>
when "Start Instance": start_instance(task.realms[0])<br>
...<br>
end<br>
rescue RealmFull => rfe<br>
if task.realms.size > 1<br>
task.realms.delete(0)<br>
perform_task|(task)<br>
else<br>
raise rfe<br>
end<br>
end<br>
end<br>
<br>
def start_instance(realm)<br>
...<br>
# Instance failed realms full<br>
raise RealmFull.new(:message => "Could not start instance
realms are full")<br>
end<br>
end<br>
</blockquote>
<br>
In the 2nd example, we can recover from realm full problem by
catching the RealmFull exception, and retrying the start_instance
with the next realm in our list until our list is exhausted. Once
the realm list is exhausted there nothing more we can do at this
point so we raise the RealmFull exception up to the next level,
where it is then handled accordingly.<br>
<br>
Of course you could say why not just catch all exceptions in the
first example and react accordingly. Well yes in this example you
could do that, but imagine start_instance can fail in many places:
realm full, hwp does not exist, ami not found etc...<br>
<br>
<br>
Example2: Catching/Handle generic exceptions at the top of the chain<br>
<br>
Once we have defined custom exceptions we can then manage them at
the top level, categorised into particular genres. An example where
this already happens in conductor is in the API, where we have
defined an APIException, all exceptions throw from within the API
controllers extend this exception and all other exceptions should*
be wrapped in an appropriate APIException. This allows us to handle
all APIExceptions at the top level. ApplicationController has a
filter than catches all APIExceptions that are raised this far up
the stack. It then creates a proper XMLResponse, with HTTP status
code, Error Code and Error Message.<br>
<br>
You can imagine we have lots of exceptions genres, e.g.
UIException. When application controller catches a UIException it
knows it must display a message to the user.<br>
<br>
ConductorException, might simply just log the exception trace and
carry on as normal.<br>
<br>
And so on.<br>
<br>
--- Exception Handling<br>
<br>
In order to add in the power of exceptions we need to all agree on a
particular approach.<br>
<br>
Here are 3 rules that if we stick to will let us handle exceptions
more wisely.<br>
<br>
1) Never Swallow Exceptions (Unless they are an expectation)<br>
<br>
2) Try to handle the exception locally.<br>
<br>
3) If 2 is not possible, pass the exception up the stack, (wrapping
it into something understandable to the upstream method, if
neccessary).<br>
<br>
If everyone sticks to these 3 rules. Exceptions will be handled and
recovered from in all cases possible, those that are not will
eventually work their way up to the application controller where
they will be handled appropriately, according to their genre.<br>
<br>
</body>
</html>