Blog




The exception design best practice Design Exception Class Hierarchy provides two reasons to design a new exception class for a Java application or library:

  1. You want to catch an exception and handle it individually.
  2. You require specific information to be passed to the exception to document the context in which the exception has been raised.

 

 

For a discussion, why having more than one reason may put us into trouble, have a look at Why is having two Reasons a Problem at all?.

Not a Problem for the Java Core Library

Java exceptions in the core library do not have to tackle the second reason. They usually do not contain data fields and therefore add specific information directly into the message text (or do not have any message text at all). Examples are NullPointerException or MalformedURLException. It is the responsibility of the developer, who throws the exception to text the message individually.

throw new IllegalStateException(
  String.format("This is my message to signal"
      + " that '%s' is not initialized properly.", it));

When there is additional information as part of the exception instance that can be retrieved by getter methods, this information is not necessarily part of the message text. SQLException is an example for this kind of exception. Exception information and the exception message are independent of each other. Therefore you can reuse the same exception class in many situations. So it is no big deal that this exception serves two purposes.

Something to consider for smartics-exceptions

smartics Exceptions puts much effort to help application developers to design exceptions with searchable exception codes and meaningful messages. The act of throwing of the exception should be easy and detached from the work of wording the encountered problem into a message text. Therefore smartics-exceptions moves the message out of the exception class and associates the message with the exception by an exception code. The developer now only uses the exception and the code, adds the relevant information to it and has not to bother with any message texts.

throw new NotInitializedException(COMPONENT_A_CODE, it);
 

Well, in fact someone has to write the exception message text. So the developer adds some more artifacts (code enum, message bundle and the exception class). But these steps have to be taken whenever a new exception class has to be created. You'll find an overview on this in What is smartics-exceptions all about?

So why are the two reasons to design an exception type a problem for smartics-exceptions? Since you attach the message to the exception type by the use of a code, the chance of reuse of the same exception is reduced. The exception code points to a message template that requires specific information for its placeholders. This is especially true, since the messages are often quite detailed and therefore require specific pieces of information to merge into the message template. So although you may change the code to select a different message, the message template often does not match the provided information of the exception type. Different messages call for different information and therefore you end up with writing different exception types (i.e. Java classes).

As we have shown above, the exceptions of the Java editions 'solve' this problem by urging the developer to assemble the message text on a ad-hoc basis. That way the information stored inside the exception is decoupled from the message text. And all that is really important is the type of the exception to be caught.

Additional Layers of Abstraction

 

 All problems in computer science can be solved by another level of indirection ... except for the problem of too many layers of indirection.

David Wheeler (first part), Kevlin Henney (corollary)

 

Having two reasons for creating a new exception class should be avoided, since it makes the design more complex, less intention revealing and may lead to conflicting goals. The problem stems from the fact that an exception type has two responsibilities that are both equally stressed by smartics-exceptions. If we want to decouple these two responsibilities, we would have to attach a common information instance to the exception type during initialization. This approach has the following form:
final MessageBean info = 
  new MySpecialExceptionMessageBean(MyCode.X, "Arg1", arg2, arg3);
throw new MyException(info);

In this scenario you would have to write a new exception info container type (we call it message bean, although it is not necessary to actually add getter methods to it) for each context that provides its own set of information. With static methods and imports this can be shortened to:

throw new MyException(info(MyCode.X, "Arg1", arg2, arg3));

We solve our problem by adding additional levels of abstraction. It is just a matter of how much code we want to write to ease the act of throwing of exceptions and enabling single responsibilities.

The approach of providing separate message beans is currently supported by smartics-exceptions as an alternative way of designing exceptions. Please refer to Separation of Exception Type and Information for more information on this topic.

Worth the Effort?

Under which conditions is the introduction of message bean types worth the effort?

If you are using the same message bean in many contexts, this usually pays off. If you have many message beans that you only use in one or two situations, then it is only a matter of coding style and readability.

If you are using the message bean in different situations, especially in the case of logging, you my see the message bean not as an additional effort, but as an opportunity to store the relevant information of a certain context. If it is possible to design these message beans this way, they may be used by a class of exceptions (e.g. all exceptions dealing with security) and logging statements (logging in each security service). The containers are in these scenarios not designed for a specific occasion (as exceptions are), but for a context in which different kinds of exceptions and logging events occur. The context in this case naturally allows to specify more information than is actually reported in an exception or log event. This allows reuse and makes this approach less work intensive.

But this can also lead to more complicated code, if the context of information that is passed to an exception is only partially filled. If looking at the code does not reveal if all information required by the exception is actually present, you may find yourself looking at a log that lacks the information required to analyze an error condition. In this case, writing specific message types may make your maintenance time easier.

Since neither approach may fit each situation within your application, extending AbstractMessageException or AbstractMessageRuntimeException allows to construct a solution for each exception situation individually.

 

You may have a look at two example projects that use smartics-exceptions: One is a library called smartics-exceptions-sample-lib, the other an application that uses this library: smartics-exceptions-sample-app

Conclusion

smartics Exceptions allows to separate between exception types and exception context information using message beans. It is a design decision whether to use this approach or not. The decision can be made for each exception type of a project individually.


Link

Link

Posts