*in special cases
Throwing a subclass of java.lang.Error can be useful, especially where there is a chance that developer code is catching Exception (thus catching all RuntimeException types as well). Most UI applications are built over a standard framework of Database Access Layers, screen base classes and Services which developers use to avoid duplicating basic functionality. If one of these important pieces of framework (e.g. the back-end services) becomes totally unavailable the application has three choices:
Try and struggle on through even though the Service is unavailable. This creates a poor user experience, because the application may continue to fail awkwardly.
Have code throughout to detect this problem and pass it on. This would mean rethrowing ServiceException types back up through many layers of wrapper classes.
Throw a new type of Error – ServiceError, which is caught in a single place by the application (e.g. in a single configuration entry in web.xml) so the user is redirected to a “System unavailable, please try again later” screen.
The third option is the best solution.
Say for example an application to manage timesheets, called “Timesheet”.
Normally there would be some Error types created for an application:
Project Error types are fewer in number than the Exceptions but are more powerful, since they are able to break out of normal try/catch blocks. Error types don't get caught in more than a few places in the framework.
The base class for all Error types in the Timesheet application would be a TimesheetError so application errors could be differentiated from other unexpected errors.
/**
* Base class for all can-not-continue errors in the Metro application
* Never throw it, throw a subclass instead
*/
public class TimesheetError extends Error
{
:
}
/**
* Web Service hasn't responded to heartbeats recently or is known
* to be completely down.
* Can't finish rendering screen
*/
public class ServiceDownError extends ServiceError
{
:
}
/**
* Base error type for authentication and security problems
* Doesn't make sense to finish rendering the screen
*/
public class AuthenticationError extends ServiceError
{
:
}
/**
* For some reason the user session isn't valid.
* Doesn't make sense to finish rendering the screen
*/
public class InvalidCredentialsError extends AuthenticationError
{
:
}
/**
* Licence for using the software has expired. This error exists so
* general screen developers don't need to worry about license expiry
*/
public class LicenceExpiredError extends AuthenticationError
{
:
}
TimesheetError represents situations that the common developer is not expected to handle. For example, imagine a web application with 200 screens. Should each of those screens be handling the rare case where the database is completely unavailable? No – it should be handled in one place by the framework. Error types do not need to be declared or caught, so are hidden from the general codebase.
Following is an example of a web application that catches error types in one place, using a simple entry in web.xml:
<web-app>
:
<error-page>
<exception-type>com.hyro.timesheets.TimesheetError</exception-type>
<location>/error.html</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/unexpected.html</location>
</error-page>
</web-app>
In the example, all application Error types are redirected to a Controller at /error.html which can display user friendly error messages depending on the type of problem it is.
To take a similar example from RuntimeException types, if every piece of code had to deal with an OutOfMemoryException (which is technically possible at any time and not recoverable) the source code would be an unmaintainable mess of try/catch(OutOfMemoryException) blocks. Handle serious errors in one place and free the developer from having to think about them.
Tip: It doesn't matter what kind of critical software module an application depends on to apply this rule, database, web service, email server, payment gateway. If a problem occurs and the desired activity can't be completed and can't recover, use Error types to elegantly handle the situation in one place.
A java.lang.Error (or any exception) thrown within an EJB layer will not automatically bubble back to the calling client (it is wrapped in a RemoteException), so Error should not be thrown in the EJB Layer.