Using smartics-exceptions

What are best practices for using the smartics-exceptions library?

Dependency Magnet

If all error codes of an application are stored in one enumeration, this enumeration becomes a, as Robert C. Martin calls it, dependency magnet. Any change to the enumeration will require a redeployment of all modules that use it. Developers will get reluctant to make changes to it. Therefore it is a best practice to provide an enumeration per package dealing with a certain kind of problem. Changes to the enumeration will then only have local effect.

Error Code Index

For small projects it is helpful to create a class of constants listing your exception code ranges. This makes it easy to check which ranges are already used. Since you are designing your error codes in advance, this class should not be subject to many changes.

The error code index class may look like this:

package de.smartics.example.util;

/**
 * Constants to define number code ranges.
 */
public final class ApplicationNumberCode
{
  /**
   * The start number of the system codes.
   */
  public static final int SYSTEM_CODE_START = 0;

  /**
   * The start number of the security codes.
   */
   public static final int SECURITY_CODE_START = 1000;

  /**
   * The start number of the validation codes.
   */
  public static final int VALIDATION_CODE_START = 2000;

  // ... add further code ranges here...

  /**
   * Constant class.
   */
  private ApplicationNumberCode()
  {
  }
}

This will work well for small applications. On a larger scale you may provide some of those classes for each module to keep the coupling low. This will also help to fend off the Dependency Magnet problem.

Message Bundles for Exception Codes

The message bundles should have the base name of the codes implementation (usually an enumeration) with the suffix Bundle and be placed in the same package.

de.smartics.example.service.security.SecurityServiceNumberCodeBundle.properties

de.smartics.example.service.security.SecurityServiceNumberCode.java

Never analyze the Code Parameter in the Catch Block

Should we provide one code number per exception or is it appropriate to have a couple of codes for one exception class? Basically it is convenient and effective to have one exception class that is configured with different codes, provided that

  1. the logical exceptions (discriminated by the codes) sharing the same class do not require different exception information (modeled as properties of the exception class).
  2. handling the exception after catching it, does not require to look up the code.

If any of the above facts are offended, write a new exception class. If an exception class only supports one code and will never happen to support more than one code, then hard code it into the exception.

And stated positively: Write different exception classes only if the exception is to be caught in a catch block and handled individually. Following this rule keeps the number of exception classes small.

This tip is related to Do not analyze caught Exception.

One Code Enumeration per Component

We recommend to write on code enumeration per component of your software system. If your software system is small, one code enumeration may be sufficient. If it is composed of large components, you may want to subdivide components, giving them names like componentA/subcomponentX.

You may decide to continue counting independent of the component:

  • componentA-1000
  • componentB-2000
  • componentC-3000

or you may start the number code with each component new from one:

  • componentA-1000
  • componentB-1000
  • componentC-1000

It mainly depends upon whether you want to have a unique number that stands for its own or if you have numbers with a alphanumeric prefix (i.e. the component ID).

Adding global Information

If your want to provide specific Information (e.g. host and client information or business IDs) for all of your exception classes the recommended way to do this is as follows:

  1. Implement a new class (for this example let's call it MyExceptionInfo) that provides the information you require. Make sure the class is serializable since it will be referenced by the application exceptions to be defined. The requirement of a class of its own for this information derives from the fact that this information is required by the runtime and checked exception.
    public final class MyExceptionInfo implements Serializable {
      ...
    }
  2. Inherit from CoreException and add accessors to retrieve your information (an instance of class MyExceptionInfo): MyCoreException
    public interface MyCoreException extends CoreException {
      MyExceptionInfo getMyExceptionInfo();
    }
  3. Extend AbstractCoreRuntimeException (or a subclass if you require e.g. I18N support) to implement your application runtime exception class implementing the MyCoreException interface.
    public class MyRuntimeException extends AbstractCoreRuntimeException implements MyCoreException {
      public MyExceptionInfo getMyExceptionInfo()
      {
        ...
      }
    }
  4. Extend AbstractCoreException to implement your application checked exception class implementing the MyCoreException interface.
    public class MyRuntimeException extends AbstractCoreException implements MyCoreException {
      public MyExceptionInfo getMyExceptionInfo()
      {
        ...
      }
    }

What about alternative solutions? A generic typesafe solution would require to have a generic base exception class which is not allowed by the Java specification (please refer to No Generic Exceptions for details). A generic solution using a plain Object reference for an arbitrary payload or a solution similar to the ConstraintViolation class of the validation framework has also been discussed, but rejected. The reason for this is the complexity and the missing aesthetic (cast or wildcard is required). Last but not least either solution would provide little or no gain, since in the context of exception handling, it is recommended to Design Exception Class Hierarchy. So a base class for the application exceptions is already provided and common exception information can be introduced typesafe and naturally.