Tim Ellison wrote:
Part of the patch for HARMONY-6346 includes some improvements to the way
we handle localized exception messages. That prompted me to write down
a few thoughts...
I don't really like the way we do I18N of exception messages. We create
message string like this
throw new IOException(Messages.getString("archive.1E"));
One way to handle this could be to use an exception manager class.
Rather than carrying out the lookup on the error message at the time the
exception is thrown, instead associate the exception object with the
error message key using a exception manager class. Then in the
getMessage(), getLocalizedMessage() etc. methods the exception will call
into the exception manager to get it's message (and can cache it locally
in case of future lookups) if one exists.
So instead of:
throw new IOException(Messages.getString("archive.1E"));
you might have (here create() returns the exception passed to it as the
first parameter):
throw ExceptionManager.create(new IOException(), "archive.1E");
Once the exception is caught and it's message string queried, it will
call ExceptionManager.getMessage(this) to see if it has an associated
message available. This way the error message lookup will only happen if
the message is actually queried. The references in the ExceptionManager
could be soft so that if the exception is not caught or is discarded,
then the ExceptionManager does not unnecesarily hang onto it's error
message.
The string "archive.1E" is a key into the resource files loaded by the
Messages class based on the current locale.
1) I have no concrete proof, but I'm prepared to bet that 90% of the
exceptions thrown never get asked for their message string. Yet we are
setting it eagerly - mainly because there is no mechanism on exceptions
to answer the message lazily.
Simple messages may not be too bad, but we also have formatted messages
where we replace token in the message with actual parameters. A
particular bad example requires formatting more argument strings, e.g.
throw new IllegalArgumentException(
Msg.getString("K031e", new String[] { "query", uri.toString() }));
in this case we format the URI and format the message string, and
probably never use the answer.
For this example, the create() method could be made to accept variable
arguments. It's first argument would be the exception object, the second
would be the message identifier, and any subsequent arguments would be
the values to be inserted into tokens in the message string. This would
mean that in the above example the uri.toString() would still be
evaluated at the time the exception was created, but the lookup and
formatting of the error string would only be done when the actual
exception message was queried. (I havn't looked deeply at this case so
it may be more complex then that).
Regards,
Oliver
2) The use of ResourceBundles for handling the strings seems like
overkill too.
Each time we need a string, we load up the entire set of error messages
for that module into a Properties (i.e. hash table) and hold onto them
forever.
The HARMONY-6346 patch makes the resource file soft referenced so that
may help, but I can't help thinking there is a simpler mechanism for
loading the one or two strings that we need rather than the whole lot +
data structure each time.
Regards,
Tim
--
Oliver Deakin
Unless stated otherwise above:
IBM United Kingdom Limited - Registered in England and Wales with number 741598.
Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU