What are best practices for using the smart-exceptions library?
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.
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 the Dependency Magnet problem.
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
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
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.
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:
public final class MyExceptionInfo implements Serializable { ... }
public interface MyCoreException extends CoreException { MyExceptionInfo getMyExceptionInfo(); }
public class MyRuntimeException extends AbstractCoreRuntimeException implements MyCoreException { public MyExceptionInfo getMyExceptionInfo() { ... } }
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 aestetic (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.