Allowing "plugin" programmers to subscribe to messages I am having difficulty deciding how to implement an exception handling strategy.

I am using an observer pattern to allow "plugin" programmers to subscribe to Messages. These subscribers generally log a unique error (and do some other stuff) under an exceptional circumstance during handling the message. The code snippet below is an example of what a common implementation (and its interface) looks like:

// API interface
interface IMessageListener {
   void onMessage(Message m);
}

class StuffMessageListener implements IMessageListener {
    ...

    @Override void onMessage(Message m) {

        try {
            Stuff s = stuffDAO.getStuff(..); // throws SQLException

            ...

            m.reply(true);
        } catch (SQLException e) {
            // if an exception happens, log it, and reply to the message
            log.error(A_UNIQUE_ERROR_CODE, e);
            m.reply(false);
        }
    }
}


What irks me is that the catch block has become very redundant code, copied throughout 95% of the ~50 existing listeners. Can we come up with something better?

A good solution should:


be easy to use/understand for an amateur programmer
keep the programmer aware of the existence/potential of exceptions
minimize the chance of a programmer accidentally "swallowing" an exception
reduce the amount of "boilerplate" code


There is a subtle requirement needed for the solution. The UNIQUE_ERROR_CODE should be unique across all declarations of the listeners. That way, I could gain statistics based upon where the error was caught across all listeners.

If the UNIQUE_ERROR_CODE is buried underneath an abstract class, or is used outside onMessage(..), I will lose the needed "uniqueness."
Allow throwing exceptions

Do you really think your user is capable of handling all the exceptions?

interface IMessageListener {
   void onMessage(Message m) throws Exception;
}


Of course if some plugin wants to handle the exception, nothing prevents he/she of using try/catch. But no one is forced to do so and if, according to your knowledge, the catch block is almost always the same, put it outside in the code calling onMessage().

Give your user a simplified adapter

abstract class ThrowingMessageListenerAdapter implements IMessageListener {

    @Override
    public void onMessage(Message m) {
        try {
            doOnMessage(m);
        } catch (SQLException e) {
            // if an exception happens, log it, and reply to the message
            log.error(A_UNIQUE_ERROR_CODE, e);
            m.reply(false);
        }
    }

    protected abstract void doOnMessage(Message m) throws Exception;

}