bdd0a41aed
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@668359 13f79535-47bb-0310-9956-ffa450edef68
158 lines
13 KiB
HTML
158 lines
13 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html>
|
|
<head>
|
|
<title>Exception Handling Guidelines for The Java Runtime</title>
|
|
</head>
|
|
<body>
|
|
<h1>Java Runtime Exception Policy</h1>
|
|
<p> The key tenet of this exception policy is that exceptions should be designed with an eye
|
|
toward what the catch clause would likely do with the exception. The three main cases
|
|
are: </p>
|
|
<ul>
|
|
<li>Code Exception: Code can work around the discovered problem</li>
|
|
<li>User Exception: Problem to be remedied by a human (e.g. Administrator)</li>
|
|
<li>Assertion Exception: Problem remedied by human fixing a bug in the code</li>
|
|
</ul>
|
|
<h2>Code Exceptions</h2>
|
|
<p> These are exceptions where it is expected that some calling code may be able to
|
|
completely handle the exception, without involvement of any user. In other words, the
|
|
exception is of an alternate way of returning a value. There is a reasonable chance that
|
|
calling code (maybe a couple levels up) will be able to catch the exception and either
|
|
try again or try some other approach to accomplishing its job. Note that there may be no
|
|
way of knowing whether the caller will be able to figure out a different approach to
|
|
handling the situation. This is especially true in reusable utility code. In these
|
|
cases, the exceptions should be considered to be code exceptions. The code that handles
|
|
the exception might just turn it into a different kind of exception.</p>
|
|
<h3>Implications</h3>
|
|
<p>In general, code exceptions should be checked exceptions. They should be named based on
|
|
what happened, rather than based on who is throwing the exception. If the exception is
|
|
well named, it should be possible for the exception to be present on signatures at
|
|
several levels of a call stack and still make sense (e.g. ServiceUnavailableException).</p>
|
|
<p>There are some cases where code exceptions should not be checked exceptions. If code
|
|
cannot reasonably be expected to recover from an exception, it should be unchecked,
|
|
Also, iIf a large fraction of the methods in the code would need to declare the
|
|
exception, then its declaration doesn't add much value and so it should be a
|
|
RuntimeException so it doesn't need to be declared. One example of this kind of
|
|
exception might be a RetryException. This exception might occur on some kind of resource
|
|
conflict where retrying the transaction is likely to solve it. Since it is solved
|
|
without human involvement it is still a "code exception". </p>
|
|
<h2>User Exceptions</h2>
|
|
<p> These are exceptions that signal a problem that will be handled by a person, so the most
|
|
important component of the exception is the message, rather than the type of the
|
|
exception. Unfortunately, the code that throws the original exception often will not
|
|
have enough information to give a meaningful message to the user that has all the
|
|
necessary context. The typical "user" in this situation is an administrator, where a
|
|
stack traceback wouldn't be very helpful. Because of this, it is important that code be
|
|
littered with try/catch blocks that do no more than add context to the exception message
|
|
and then rethrow.</p>
|
|
<p>In a previous project this was done by having a base UserException class that had an
|
|
array of messages, rather than just one message. For example, code that parses an SCA
|
|
subsystem file might have a rethrow that just adds "While parsing the xyz subsystem
|
|
file". That is a message that could not be generated by the code that discovered the
|
|
problem (say an XML parsing problem), so a combination of the original message (e.g.
|
|
"Missing end tag") and the higher level message ("while parsing the xyz subsystem file")
|
|
are both necessary for know what happened. Naturally it can be any number of levels
|
|
deep.</p>
|
|
<p> The handling code for a user exception will somehow notify a user of the message and
|
|
then possibly go on. There should be different kinds of exceptions when there need to be
|
|
different ways of handling of the message or different ways to continue. Different ways
|
|
to report the error: In a server, user exceptions can often be divided according to
|
|
fault: </p>
|
|
<ul>
|
|
<li>It's the fault of the client code that is sending the incoming message (e.g. SOAP
|
|
faults).</li>
|
|
<li>It's the fault of the code or configuration that is handling the message. </li>
|
|
</ul>
|
|
<p> If the problem is the fault of the client code, then the message needs to be reported
|
|
back to the client code in a format appropriate for the client. If the problem is the
|
|
fault of the server code or configuration, then only a vague "I've got a problem here"
|
|
message should be sent to the client and the real exception message should be logged
|
|
and/or sent to an administrator. Because of the two different ways of handling the
|
|
problem, there should be different exception types. For example, ClientException could
|
|
be used for exceptions that signal problems that are the client's fault. </p>
|
|
<p> The remaining user exceptions are typically problems with configuration or the
|
|
environment. Some of them will be severe enough that the entire application needs to be
|
|
brought down, while others could be handled by just logging the problem and going on.
|
|
This difference implies that there needs to be a different exception type. Advanced
|
|
Scenario: In the case of session-scoped services, the problem is likely to require that
|
|
the instance of the service be put into an error state (like paused). This is because
|
|
subsequent messages for the service have been sent on the assumption that the previous
|
|
message actually gets processed. If some configuration error prevents a session-scoped
|
|
service from handling a single message, all future (async) messages for that service
|
|
instance should be queued up so they can be processed once the problem has been solved. </p>
|
|
<h2>Assertion Exceptions</h2>
|
|
<p> Assertion exceptions are exceptions that result from a bug in Tuscany and as such are
|
|
also intended to be solved by humans, but in this case the humans are us --are the
|
|
developers of the SCA runtime. In these cases the message isn't nearly as important,
|
|
since the stack traceback provides valuable context. If an assertion exception occurs
|
|
little can be known about the state of the server. If we wanted to be safe we would say
|
|
that assertion exceptions always bring down the entire server. However, we could play it
|
|
a little looser and say that assertion exceptions only bring down the application in
|
|
which they are discovered. </p>
|
|
|
|
|
|
<h2>Guidelines</h2>
|
|
<p> The following are a set of guidelines based on the above exception philosophy: </p>
|
|
<h4>1. Checked vs. unchecked exceptions</h4>
|
|
<p> Unchecked exceptions should be used when an error condition is not recoverable. Checked
|
|
exceptions thrown by third party libraries that are not recoverable should be wrapped in
|
|
unchecked exceptions rather than being propagated up the call stack. For example, an
|
|
IOException raised when reading a file might be wrapped in an unchecked LoadException
|
|
containing the name of the file.
|
|
Unchecked must always be Javadoced and declared in the throws clause of a method. </p>
|
|
<h4>2. Assertion exceptions should use the standard JDK assert facilities</h4>
|
|
<h4>3. Any exception thrown to user code must extend the appropriate Exception as defined
|
|
by the specification. This will typically be a runtime Exception.</h4>
|
|
<h4>4. No other Exceptions should be thrown to user code. Each user API method should
|
|
catch any internal exceptions and wrap them in the applicable Exception defined
|
|
by the specification. Internal exceptions must ultimately extend either TuscanyException
|
|
or TuscanyRuntimeException.
|
|
<h4>4. When possible, create clear package exception hierarchies</h4>
|
|
<p> In most cases, packages should have a clear exception hierarchy with abstract root
|
|
checked and unchecked exceptions which more specific concrete exceptions extend.
|
|
Declaring the root package exceptions abstract avoids code throwing exceptions which are
|
|
too general. Creating an exception hierarchy allows client code using a particular
|
|
package to choose the level of exception handling granularity (which in turn simplifies
|
|
the client code by avoiding unwieldy try..catch clauses). </p>
|
|
<h4> 5. Preserve all stack trace information and the original exception</h4>
|
|
<p> Exceptions must always preserve the stack trace and original exception except under
|
|
special circumstances. When wrapping exceptions to propagate, never modify the stack
|
|
trace and always include the caught exception as the cause.</p>
|
|
<h4>6. Only include local information pertinent to the failure</h4>
|
|
<p> For I18N, contextual information stored in the Exception should not be localized. It
|
|
should comprise only data pertaining to the cause, such as the name of the artifact as
|
|
above, or a key that can be used by the top level exception handler. This is needed
|
|
because the locale used to render the exception may be completely different from the
|
|
locale used by the code raising the exception. For example, an exception may be thrown
|
|
on a system whose default locale is German, logged to the system log in English but
|
|
displayed to the end user in French, Japanese, whatever their native language is. </p>
|
|
<h4>7. For exceptions that require contextual information from various code layers, either
|
|
wrap exceptions or create exceptions that can accept additional context as they are
|
|
propagated up the call stack.</h4>
|
|
<p> If a failure requires information from multiple levels, e.g. “there was an error setting
|
|
property X on component Y in module Z” do one of the following. If the initial exception
|
|
should be wrapped as it is propagated (e.g. the exception occurs at a library boundary),
|
|
add additional context information in the wrapping exception(s). If the initial
|
|
exception can be propagated, include methods for adding additional context information
|
|
as the exception is rethrown up the stack. For example, the previous failure scenario
|
|
could result in the following exception handling strategy: </p>
|
|
<ul>
|
|
<li> A component property is configured with an invalid integer type</li>
|
|
<li> The property value parsing code attempts to load an integer value using parseInt(),
|
|
resulting in a NumberFormatException</li>
|
|
<li> NumberFormatException is wrapped in an InvalidParameterException (IPE) containing
|
|
the name of the property.</li>
|
|
<li> IPE extends a more general ConfigException, which has setters for adding additional
|
|
context information such as component and module names</li>
|
|
<li> As the IPE is thrown up the stack, the component and module parsers provide
|
|
additional context information.</li>
|
|
<li> The configuration loader then wraps the IPE in a ConfigLoadExeption and provides
|
|
the source from which the configuration is being loaded.</li>
|
|
<li> The UI being used to load the configuration reports the error to the user and
|
|
displays the appropriate contextual information</li>
|
|
</ul>
|
|
<h4>8. getMessage() must return unformatted context info. If the Exception contains multiple
|
|
context fields they should be surrounded in square brackets and separated by commas,
|
|
e.g. "[ property X, component Y, module Z ]"</h4>
|
|
<h4>9. Do not override the behaviour of Throwable.toString() and Throwable.printStackTrace()</h4>
|
|
<h4>10. The java.lang.Exception base class is Serializable so all subclasses must provide
|
|
a serial UID. Any context fields must be Serializable and should be defined in the
|
|
base java namespace for JDK1.4.</h4>
|
|
<h4>11. Exceptions that wrap other Exceptions should ensure that any wrapped Exception can
|
|
be deserialized in a client environment. This may require providing a custom
|
|
writeObject method to extract any context information from the wrapped Exception
|
|
during serialization; at a minimum the message should be preserved.</h4>
|
|
</body>
|
|
</html>
|