Custom Exception Classes

The purpose of a custom exception class is to integrate the look-up of localized message strings in a custom message catalog into the mechanism used for error reporting in the client infrastructure. If you only need one error message catalog, you will only need one custom exception class, but there is no restriction on the number of exception classes or message catalogs you can create.

Implementing custom exception handling using a custom exception class is formulaic. As the custom exception class must integrate into the existing message reporting system, only numeric message identifiers are supported for custom exceptions and there is very little room for deviation from the prescribed approach. You cannot, for example, use literal message strings in your code, you must use references to externalized strings.

Here is an example of a custom exception class:

Figure 1. Custom Exception Class
public class CustomConversionException
       extends ConversionException {

  private static final MessageLocalizer MESSAGE_LOCALIZER
    = new CatalogMessageLocalizer("custom.ErrorMessages");

  public CustomConversionException(int messageID) {
    super(messageID);
  }

  public CustomConversionException(int messageID,
                                   String[] messageArgs) {
    super(messageID, messageArgs);
  }

  public CustomConversionException(int messageID,
                                   String messageArg) {
    super(messageID, messageArg);
  }

  public MessageLocalizer getMessageLocalizer() {
    return MESSAGE_LOCALIZER;
  }
}

This class extends ConversionException and implements a number of constructors simply by invoking the equivalent constructors in the super-class. You only need to implement the constructors that you intend to use, the rest of the constructors in the super-class can be ignored (Java classes do not inherit constructors, hence the need to re-implement them). The available constructors are described in the JavaDoc. Next, it defines a static MessageLocalizer field and instantiates it with a CatalogMessageLocalizer object that takes your custom catalog name as its argument. The getMessageLocalizer method then returns this static object. That is all there is to it.

When you throw exceptions of this type, you need to pass your message identifier and optional arguments to the relevant constructor. You can define constants for your numeric message identifiers in this class if you wish. Your message strings can contain placeholders such as "%1s", "%2s", etc., to be replaced by the argument strings (only string types are supported). For an array of arguments, "%1s" will be replaced by the first argument in the array (index zero), and so on. The special argument "%0s" can be used to represent the name of the field in error, but you will not need to provide any matching argument string for that value; it will be substituted automatically. You can also use the same placeholder several times in a single message if you want the same value to be inserted in more than one place. Here is a sample message catalog file containing a single message:

Figure 2. Custom Message Catalog
-200000=ERROR: The field '%0s' contains an invalid value '%1s'.

The file is a standard Java properties file where each line contains a numeric identifier and a message string separated by an equals character. A collection of properties files with the same base name but with locale codes appended is treated as a single message catalog. The custom exception class in the example above refers to the message catalog as "custom.ErrorMessages", so the properties files should be located on the Java classpath in the custom package folder and in files named ErrorMessages.properties, ErrorMessages_en_US.properties, ErrorMessages_fr_CA.properties, etc., as you would do for any other custom properties files. There should be one properties file for each locale that your application supports. The selection of the correct locale-specific properties file at run-time is completely automatic once you have written your custom exception class as shown above.

Ensuring that these files end up on the classpath is simply a matter of placing them in their appropriate package folders below your web application's <client-dir>/<custom>/javasource folder, where custom is the name of a custom component. (see Project Folder Structure for details). The Java source files for your custom exceptions should also be placed below the <client-dir>/<custom>/javasource folder in the appropriate folders for the package names you have used.

When throwing a custom exception, the code will look like this (assuming you have decided not to use constants for your error message identifiers):

Figure 3. Throwing a Custom Exception
throw new CustomConversionException(-200000, myInvalidValue);

Remember, it is not necessary to pass any argument corresponding to the "%0s" placeholder; it will be calculated and substituted automatically.

Numeric Message Identifiers: When creating message catalog files, try to ensure that the error numbers do not conflict with the numbers of existing Cúram error messages, as this may cause confusion when errors are being investigated. Values below -200000 should be safe to use, though conflicting numbers will not actually cause any application problems, as the message catalogs are separate from those used by the infrastructure.

If you examine the constructors of the ConversionException class, you will note that many accept a java.lang.Throwable object as the last argument. You can implement similar constructors and pass Throwable objects (usually other exception objects) to your custom exceptions when you want your custom exception to include the exception that caused it. This is often very useful as error messages for both exceptions will be reported automatically and both stack traces will be included on an application error page if the error page is required. In fact, there is no imposed limit to the length of the chain of exceptions that can be built this way; the exception that you add to your own may already contain a reference to another exception, and so on.

This example show how you can even report two separate error messages at once. Perhaps one is a generic message that states that a field does not contain a valid value and another suggests the expected format for that value. You will have to implement the appropriate constructor to support this, but the reporting mechanism is automatic.

Figure 4. Throwing Multiple Exceptions
throw new CustomConversionException(
    -200000, myInvalidValue,
    new CustomConversionException(-200003));