In this article, part of the series to introduce the Java library smartics Exceptions, we want to give an overview over the differences using messages beans or following the traditional path of adding exception context information to the exception instance. With this information it will be easier to decide which way to go.
Message Bean
A message bean is an implementation of MessageBean
. It provides information about a context. A context references information that is relevant to a given runtime situation. A message bean may be information for an event to be logged or for an exception to be raised.
This is a sample implementation of a simple message bean:
@ParentMessageParam("cause=causeMessage:message")
public class ImportMessageBean extends AbstractMessageBean {
private static final long serialVersionUID = 1L;
@MessageParam
private final String sourceId;
public ImportMessageBean(final String sourceId) {
this(null, sourceId);
}
public ImportMessageBean(
final Throwable cause, final String sourceId) {
super(ResourceCode.IMPORT_FAILED, cause);
this.sourceId = sourceId;
}
public static ImportMessageBean failure(
final Throwable cause,
final String sourceId) {
return new ImportMessageBean(cause, sourceId);
}
}
The message bean extends AbstractMessageBean
and therefore concentrates on providing the context information it specializes on: a source ID in form of a String.
The factory method is optional and simply shortens the statement to throw the exception.
Exception Type
Writing a message bean based exception type is quite simple:
public class ImportException
extends AbstractMessageRuntimeException {
private static final long serialVersionUID = 1L;
public ImportException(final ImportMessageBean messageBean) {
super(messageBean);
}
}
In contrast, instead of one simple constructor expecting an instance of the message bean, exceptions not based on message beans, require constructors that allow to pass in the exception context information. This is the traditional approach to declare exceptions with Java:
@ParentMessageParam("cause=causeMessage:message")
public class ImportException
extends AbstractLocalizedRuntimeException {
private static final long serialVersionUID = 1L;
@MessageParam
private final String sourceId;
public ImportException(final String sourceId) {
this(null, sourceId);
}
public ImportException(
final Throwable cause, final String sourceId) {
super(cause, ResourceCode.IMPORT_FAILED);
this.sourceId = sourceId;
}
}
Exception Code and Message Bundle
The exception code enumeration and message bundle (a properties file) are not shown, since the implementation is the same for both approaches.
Throwing Exceptions
With message beans, the act of raising an exception requires a little more effort. You have to create two instances, one exception and one message bean, either directly ...
catch (final IOException e) {
throw new ImportException(
new ImportMessageBean(e, locationString));
}
... or via a factory method:
catch (final IOException e) {
throw new ImportException(failure(e, locationString));
}
In case of none-message-based exceptions, only the exception instance has to be created:
catch (final IOException e) {
throw new ImportException(e, locationString);
}
Conclusion
It is usually easier to use exceptions that provide the context information by themselves. The additional effort usually pays off, if context information is to be used by exceptions of different class hierarchies (especially runtime and checked exceptions), or for logging and exception events alike.