Exception Concepts

We discuss some concepts used in exception handling.

Correlation Identifier

A correlation identifier (or correlation ID for short) is an ID to correlate a request with a response in an asynchronous call. For details on this concept please refer to Gregor Hohpe's article Correlation and Conversations.

The concept of an unique exception identifier is somewhat similar, because it correlates different log messages caused by the same exception (either directly or by its root cause chain), but it is also different (also see Log once). It is different because the need for exception identifiers is independent of the fact of synchronous or asynchronous calls. In the context of an asynchronous call a correlation ID is required in addition to the exception identifier. The context of the request processing requires access to both informations and both informations are passed to the logging system on demand. Where the correlation ID helps to track the execution of a request, the exception identifier tracks the occurrence of related exception log entries.

Therefore smartics-exceptions supports exception IDs, but no correlation IDs.

Exception Tags

If the same exception, with the same error code is thrown from to different locations in the code, how can one tell which location actually threw the exception? Usually the stack trace uniquely identifies the location. But if it is not available, there is the need for a special tag to mark this location and is accessible within the exception.

This tag could be passed to the constructor of the exception.

throw new MyException(code, cause, "X4223")

But it would be more convenient to tag the line and let system derive the tag automatically. JSR 308 might come up with an extension to be able to annotate statements, but it is not possible, yet.

@TagStatement("X4223") throw new MyException(code, cause)

But is that tag really necessary? If it is just to identify a particular statement, we could have a look at the top level element of the stack trace. As long as no obfuscators are used, we could take the class and line number, maybe together with the method name, and generate a unique tag on demand.

com.example.someapp.somecomp.something.SomeClass.somewhatToDo:34

Currently adding tags to exceptions is not supported. It would require that we add one additional parameter to each exception. Currently these are already:

  1. the optional root cause
  2. the error code
  3. the exception specific arguments

We discuss to add a getTag method that derives a tag from the location the exception has been thrown. This allows to identify the location where the exception has been thrown without printing the whole stack trace. In this case it is especially important to remind the best practice of not delegating throwing the exception.

Generating reports for different Audiences

Documentation on exception codes may be useful for audiences besides developers. These audiences includes the support team or the actual user of the system. The generate site report usually targets developers. How can these other audiences be supported with smartics-exceptions?

smartics-exceptions provides a Maven plugin to generate an XML report for each exception code. Such a report looks like this:

<?xml version="1.0"?>
<appcode
  xmlns="http://www.smartics.de/project/process/implementation/appcode"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <id>Test-1000</id>
  <type>error</type>
  <component>Test</component>
  <implementation>
    <class>de.smartics.exceptions.sample.MyCode</class>
    <field>ZERO</field>
  </implementation>
  <short-description>The zero code</short-description>
  <package-description>Test codes.</package-description>
  <description>The zero code.</description>
  <messages>
    <message>
      <name>Title</name>
      <text>Test Exception Zero</text>
    </message>
    <message>
      <name>Summary</name>
      <text>The name is set to {name}.</text>
      <placeholders>
        <placeholder>
          <name>name</name>
          <description>The name of the test exception.</description>
        </placeholder>
      </placeholders>
    </message>
    <message>
      <name>Detailed Information</name>
      <text>No details for the test.</text>
    </message>
  </messages>
</appcode>

The information from the report can be merge with information from other sources using XSL or other technologies.

This is also the way to go if you have a technical writer that is responsible to produce high quality documentation. The writer may use the XML report as a basis of her work. Note that the documentation written as Javadoc is always only provided in one language, the language used for Javadoc in the code. Therefore any translations to other languages also have to run this process. There is no gain in cluttering the code with documentation for different languages.

Please refer to Using SDoc for information on how to configure the exceptioncodes-maven-plugin to generate XML reports.

Faults and Contingency Exceptions

Faults and contingency exceptions are not supported directly by smartics-exceptions, but the concepts described by Barry Ruzek in his article Effective Java Exceptions can easily be used with this library. Please refer to Consider to use Faults and Contingencies for some more details.

The reasons why we do not use these concepts as part of this library are:

  1. We want to stay as close to the Java standard terminology as possible.
  2. Since every application provides its own base classes anyway, the concepts can be easily integrated in any client application.

Separation of Exception Type and Information

The traditional way to define a exception is to

  1. declare a class and
  2. declare fields to store exception information.

If you want to separate the two issues, use MessageBean and AbstractMessageException/AbstractMessageRuntimeException.

Here is a short example, where we define a hierarchy of message beans. The traditional way would design two exception classes instead.

public abstract class AbstractDbMessage
  extends AbstractMessageBean {
  private static final long serialVersionUID = 1L;

  /**
   * The URL to the database.
   *
   * @serial
   */
  @MessageParam("0")
  private final String dbUrl;

  protected AbstractDbMessage(final DatabaseCode code, final String dbUrl) {
    this(code, null, dbUrl);
  }

  protected AbstractDbMessage(final DatabaseCode code, final Throwable cause,
      final String dbUrl) {
    super(code, cause);
    this.dbUrl = dbUrl;
  }

  public final String getDbUrl() {
    return dbUrl;
  }
}
public final class DbQueryFailedMessage
  extends AbstractDbMessage {
  private static final long serialVersionUID = 1L;

  /**
   * The query that failed.
   *
   * @serial
   */
  @MessageParam("1")
  private final String query;

  public DbQueryFailedMessage(final String dbUrl, final String query) {
    super(DatabaseCode.CONNECTION_FAILED, dbUrl);
    this.query = query;
  }

  public static DbQueryFailedMessage dbQuery(final String dbUrl,
      final String query) {
    return new DbQueryFailedMessage(dbUrl, query);
  }

  public String getQuery() {
    return query;
  }
}

All messages dealing with database exceptions may be passed to the following class (if particular exception cases should be caught, a separate subclass is added).

public class DbFaultException
  extends AbstractMessageRuntimeException {
  private static final long serialVersionUID = 1L;

  public DbFaultException(final AbstractDbMessage messageBean) {
    super(messageBean);
  }
}