The error messages for a JDBC driver based off of the SimbaEngine SDK are all derived from exceptions. This article will go through how the error message system works, and how to use it when implementing your JDBC driver.

Understanding the Error Message System

There are a number of classes involved in the error message system:

  • IMessageSource
  • ExceptionBuilder
  • ExceptionUtilities
  • ErrorException
  • The many sub-classes of ErrorException

These classes work together to create a flexible error reporting system which can easily support multiple locales for your driver. To understand how they work together this article will walk through the steps of what happens when an exception is thrown in your DSII.

ExceptionBuilder and ExceptionUtilities are utility classes to make setting up and throwing exceptions easier. ExceptionBuilder can be used to automatically generate exceptions as described in the following section.

When an exception is thrown, it is typically thrown with a component ID and a message key. When the specific error message or native error code are needed from the exception, the IMessageSource and a locale are passed to the ErrorException getMessage() or getNativeErrorCode() methods. The message key, component ID, and locale are then passed to the IMessageSource and used to find the correct error message properties file, and the message key is then used to identify the correct error message to use. Additionally, there may be parameters passed to the IMessageSource to parameterize an error message. The format of the expected error message is detailed in the section below.

The base name of the error message properties file is set when registering the messages file with the IMessageSource accessed via the IDriver, and is set with the registerMessages() method. Specific locale versions of the messages properties file would be suffixed with the locale using underscores. As an example, using the base file name of “messages.properties”, the Italian locale would be represented as “messages_it_IT.properties”.

The locale is composed of a 2-letter (lower case) language code, and an optional 2-letter (upper case) country code. If a country code is included, the two codes must be separated by a hyphen (-). The language codes conform to the ISO 639-1 standard: http://www.loc.gov/standards/iso639-2/php/code_list.php. The country codes conform to the ISO 3166-1 Alpha-2 code standard: http://www.iso.org/iso/country_codes/iso-3166-1_decoding_table.htm

Examples:

  • en-US (English – United States)
  • fr-CA (French – Canada)
  • it-IT (Italian – Italy)
  • de-DE (German – Germany)
  • es-ES (Spanish – Spain (Traditional))
  • ja (Japanese)

The locale can be set from the connection string by specifying DriverLocale=<locale>, and by specifying a locale in IConnection.getLocale(). If a locale cannot be loaded from either of these methods, then JVM default locale is used. If an messages properties file for that locale cannot be found, then the en-US or non-locale-suffixed properties file will be used.

Note: Specifying the locale via the connection string will only take effect after a connection has been made, any errors that occur before that point will use the default locale, or the locale specified by IConnection.getLocale().

Using the Error Message System

To use the error system in your DSII, you must have an instance of the ExceptionBuilder available. The Quickstart and Ultralight samples both make this a singleton on their driver classes. This instance should be constructed with the component ID of your DSII, since the ID is what is used to locate the proper messages depending on what component throws an exception.

Error messages are stored in a properties file embedded in your JAR file. Locales are supported by well-known suffixes. For example, if your error messages properties file for English (the default) is messages.properties, the French version would be messages_fr_FR.properties. Your IDriver subclass should register the base error messages properties file with the IMessageSource class, along with your unique driver component ID. This associates the component ID with this message file, so that exceptions with that component ID will have their messages loaded from this file. The Quickstart and Ultralight sample drivers have examples of how this is done in their IDriver subclass constructors.

Once this initial setup work has been done, exceptions can be generated using the ExceptionBuilder instance you’ve created for your driver. The ExceptionBuilder allows you to easily create general exceptions, which have a SQL state of HY000, or custom exceptions which can have any SQL state defined by your DSII. To create exceptions with other standard SQL states, you must instantiate the exception directly.

Typically when an exception is thrown, a message identifier or key is used which identifies the error message that should be looked up when the error message is read. This key is used so that the proper error message can be looked up from the correct locale, depending on what locale has been set. There may be situations where the error message is known when the exception is created, such as when the message originates from another system, and in this case you would set the error message and native error code directly instead of using a key.

The error message format is generally of the following form – (<native_error_code>) <error_message>

  • <native_error_code> is any integer that can be used to identify the error message across languages
  • <error_message> the actual error message

In some cases, you may wish to parameterize your error messages to provide more contextual information to your users. In this case, each parameter should be identified with {n} where n is the increasing number of the parameter, starting from 0. For example:

(1) This is parameter one: {0}, and this is parameter two: {1}
(2) Another message with no parameters.
(3) Yet another message with only one parameter: {0}

As an example of what an error message might look like using the first message template see the following:

[SampleDriver][JDSI] (1) This is parameter one: 1, and this is parameter two: 2

Note: Parameter values of ‘1’ and ‘2’ were supplied for the error message. The driver is the first item shown, followed by the component the exception originated from. The native error code is shown in brackets, and finally the actual error message is shown.

Warnings

Occasionally you may have a situation where the user needs to be notified of something, but the notification should not stop the current action. An example might be that their login credentials are set to expire in 5 days, or that they’ve attempted to read a value which is being altered in some way. In cases like these, a warning would be more appropriate. Warnings let the user actions continue and allow the warning message to be returned to the user as well. When a warning is posted, the JDBC function will return as normal and the warnings will be available via the getWarnings() function on the appropriate object.

To post warnings from your DSII, you will use the IWarningListener class. There is an IWarningListener for the following core classes: IEnvironment, IConnection, and IStatement. The IWarningListener is registered once via the registerWarningListener() method when the class is created, and you can access the IWarningListener through the getWarningListener() method. The IWarningListener from the IStatement class should be used for the IDataEngine and child classes, as well as the IQueryExecutor if not using the SQLEngine. An IWarningListener is also supplied in some method signatures, and the listeners that are supplied should be used in those methods rather than the IWarningListener from the IStatement.

Posting a warning is done via methods on the IWarningListener. There are multiple methods, which method in a way similar to the exception constructors when reporting an error. Warning messages are defined in the same place as the error messages, and can have parameters and locales in the same way. You can also create custom SQL states for your warnings via the IWarningListener by using the appropriate methods. The only difference between creating a warning and creating an error is that for errors you create and throw an exception and for warnings you call a method on an IWarningListener.